diff --git a/.craft.yml b/.craft.yml index d69334cd3f94..e11b79171b56 100644 --- a/.craft.yml +++ b/.craft.yml @@ -108,12 +108,18 @@ targets: - name: npm id: '@sentry/sveltekit' includeNames: /^sentry-sveltekit-\d.*\.tgz$/ + - name: npm + id: '@sentry/tanstackstart' + includeNames: /^sentry-tanstackstart-\d.*\.tgz$/ - name: npm id: '@sentry/gatsby' includeNames: /^sentry-gatsby-\d.*\.tgz$/ - name: npm id: '@sentry/astro' includeNames: /^sentry-astro-\d.*\.tgz$/ + - name: npm + id: '@sentry/react-router' + includeNames: /^sentry-react-router-\d.*\.tgz$/ ## 7. Other Packages ## 7.1 diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index c0b943277570..a68dea235925 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -85,9 +85,9 @@ body: attributes: label: Reproduction Example/SDK Setup description: - To ensure that we can help you as fast as possible, please share a link to a reproduction example (GitHub repo or online code editor). - This enables us to quickly understand and address your issue. - If you do not post a link, kindly paste your `Sentry.init` code, so we can see how you set up Sentry. + To ensure that we can help you as fast as possible, please share a link to a reproduction example (GitHub repo + or online code editor). This enables us to quickly understand and address your issue. If you do not post a link, + kindly paste your `Sentry.init` code, so we can see how you set up Sentry. placeholder: |- https://some-JS-online-code-editor.com/my-example diff --git a/.github/ISSUE_TEMPLATE/flaky.yml b/.github/ISSUE_TEMPLATE/flaky.yml index 2e86f8ebd869..4c733b8505b3 100644 --- a/.github/ISSUE_TEMPLATE/flaky.yml +++ b/.github/ISSUE_TEMPLATE/flaky.yml @@ -19,7 +19,7 @@ body: id: job-name attributes: label: Name of Job - placeholder: "CI: Build & Test / Nextjs (Node 18) Tests" + placeholder: 'CI: Build & Test / Nextjs (Node 18) Tests' description: name of job as reported in the status report validations: required: true diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 5fbf87c67d79..cfa664b1d219 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -1,13 +1,13 @@ -name: "Install yarn dependencies" -description: "Installs yarn dependencies and caches them." +name: 'Install yarn dependencies' +description: 'Installs yarn dependencies and caches them.' outputs: cache_key: - description: "The dependency cache key" + description: 'The dependency cache key' value: ${{ steps.compute_lockfile_hash.outputs.hash }} runs: - using: "composite" + using: 'composite' steps: - name: Compute dependency cache key id: compute_lockfile_hash diff --git a/.github/actions/install-playwright/action.yml b/.github/actions/install-playwright/action.yml index 5eac05a32a2d..3bd70ff71f2a 100644 --- a/.github/actions/install-playwright/action.yml +++ b/.github/actions/install-playwright/action.yml @@ -1,5 +1,5 @@ -name: "Install Playwright dependencies" -description: "Installs Playwright dependencies and caches them." +name: 'Install Playwright dependencies' +description: 'Installs Playwright dependencies and caches them.' inputs: browsers: description: 'What browsers to install.' @@ -9,41 +9,40 @@ inputs: default: '.' runs: - using: "composite" + using: 'composite' steps: - - name: Get Playwright version - id: playwright-version - run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> $GITHUB_OUTPUT - shell: bash - working-directory: ${{ inputs.cwd }} + - name: Get Playwright version + id: playwright-version + run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> $GITHUB_OUTPUT + shell: bash + working-directory: ${{ inputs.cwd }} + - name: Restore cached playwright binaries + uses: actions/cache/restore@v4 + id: playwright-cache + with: + path: | + ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} - - name: Restore cached playwright binaries - uses: actions/cache/restore@v4 - id: playwright-cache - with: - path: | - ~/.cache/ms-playwright - key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} + # We always install all browsers, if uncached + - name: Install Playwright dependencies (uncached) + run: npx playwright install chromium webkit firefox --with-deps + if: steps.playwright-cache.outputs.cache-hit != 'true' + shell: bash + working-directory: ${{ inputs.cwd }} - # We always install all browsers, if uncached - - name: Install Playwright dependencies (uncached) - run: npx playwright install chromium webkit firefox --with-deps - if: steps.playwright-cache.outputs.cache-hit != 'true' - shell: bash - working-directory: ${{ inputs.cwd }} + - name: Install Playwright system dependencies only (cached) + run: npx playwright install-deps ${{ inputs.browsers || 'chromium webkit firefox' }} + if: steps.playwright-cache.outputs.cache-hit == 'true' + shell: bash + working-directory: ${{ inputs.cwd }} - - name: Install Playwright system dependencies only (cached) - run: npx playwright install-deps ${{ inputs.browsers || 'chromium webkit firefox' }} - if: steps.playwright-cache.outputs.cache-hit == 'true' - shell: bash - working-directory: ${{ inputs.cwd }} - - # Only store cache on develop branch - - name: Store cached playwright binaries - uses: actions/cache/save@v4 - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' - with: - path: | - ~/.cache/ms-playwright - key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} + # Only store cache on develop branch + - name: Store cached playwright binaries + uses: actions/cache/save@v4 + if: github.event_name == 'push' && github.ref == 'refs/heads/develop' + with: + path: | + ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} diff --git a/.github/actions/restore-cache/action.yml b/.github/actions/restore-cache/action.yml index e523cca6d904..7e7a3971cd7e 100644 --- a/.github/actions/restore-cache/action.yml +++ b/.github/actions/restore-cache/action.yml @@ -1,27 +1,27 @@ -name: "Restore dependency & build cache" -description: "Restore the dependency & build cache." +name: 'Restore dependency & build cache' +description: 'Restore the dependency & build cache.' inputs: dependency_cache_key: - description: "The dependency cache key" + description: 'The dependency cache key' required: true runs: - using: "composite" + using: 'composite' steps: - - name: Check dependency cache - id: dep-cache - uses: actions/cache/restore@v4 - with: - path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ inputs.dependency_cache_key }} + - name: Check dependency cache + id: dep-cache + uses: actions/cache/restore@v4 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ inputs.dependency_cache_key }} - - name: Restore build artifacts - uses: actions/download-artifact@v4 - with: - name: build-output + - name: Restore build artifacts + uses: actions/download-artifact@v4 + with: + name: build-output - - name: Install dependencies - if: steps.dep-cache.outputs.cache-hit != 'true' - run: yarn install --ignore-engines --frozen-lockfile - shell: bash + - name: Install dependencies + if: steps.dep-cache.outputs.cache-hit != 'true' + run: yarn install --ignore-engines --frozen-lockfile + shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0c9602267379..43cb9fc0d6b6 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,10 +13,10 @@ updates: schedule: interval: 'weekly' allow: - - dependency-name: "@sentry/*" - - dependency-name: "@opentelemetry/*" - - dependency-name: "@prisma/instrumentation" - - dependency-name: "opentelemetry-instrumentation-remix" + - dependency-name: '@sentry/*' + - dependency-name: '@opentelemetry/*' + - dependency-name: '@prisma/instrumentation' + - dependency-name: 'opentelemetry-instrumentation-remix' versioning-strategy: increase commit-message: prefix: feat diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 47af3cc98ace..57f3afb43c9d 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -1,4 +1,4 @@ -name: "Gitflow: Auto prepare release" +name: 'Gitflow: Auto prepare release' on: pull_request: types: @@ -48,7 +48,9 @@ jobs: - name: Prepare release uses: getsentry/action-prepare-release@v1 - if: github.event.pull_request.merged == true && steps.version-regex.outputs.match != '' && steps.get_version.outputs.version != '' + if: + github.event.pull_request.merged == true && steps.version-regex.outputs.match != '' && + steps.get_version.outputs.version != '' env: GITHUB_TOKEN: ${{ steps.token.outputs.token }} with: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aae090f76188..3929d09d01f9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -99,7 +99,6 @@ jobs: any_code: - '!**/*.md' - - name: Get PR labels id: pr-labels uses: mydea/pr-labels-action@fn/bump-node20 @@ -107,7 +106,8 @@ jobs: outputs: commit_label: '${{ env.COMMIT_SHA }}: ${{ env.COMMIT_MESSAGE }}' # Note: These next three have to be checked as strings ('true'/'false')! - is_base_branch: ${{ github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/v9' || github.ref == 'refs/heads/v8'}} + is_base_branch: + ${{ github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/v9' || github.ref == 'refs/heads/v8'}} is_release: ${{ startsWith(github.ref, 'refs/heads/release/') }} changed_ci: ${{ steps.changed.outputs.workflow == 'true' }} changed_any_code: ${{ steps.changed.outputs.any_code == 'true' }} @@ -172,7 +172,8 @@ jobs: key: nx-Linux-${{ github.ref }}-${{ env.HEAD_COMMIT || github.sha }} # On develop branch, we want to _store_ the cache (so it can be used by other branches), but never _restore_ from it restore-keys: - ${{needs.job_get_metadata.outputs.is_base_branch == 'false' && env.NX_CACHE_RESTORE_KEYS || 'nx-never-restore'}} + ${{needs.job_get_metadata.outputs.is_base_branch == 'false' && env.NX_CACHE_RESTORE_KEYS || + 'nx-never-restore'}} - name: Build packages # Set the CODECOV_TOKEN for Bundle Analysis @@ -191,12 +192,24 @@ jobs: outputs: dependency_cache_key: ${{ steps.install_dependencies.outputs.cache_key }} - changed_node_integration: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry-internal/node-integration-tests') }} - changed_remix: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/remix') }} - changed_node: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/node') }} - changed_deno: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/deno') }} - changed_bun: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/bun') }} - changed_browser_integration: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry-internal/browser-integration-tests') }} + changed_node_integration: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry-internal/node-integration-tests') }} + changed_remix: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry/remix') }} + changed_node: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry/node') }} + changed_deno: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry/deno') }} + changed_bun: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry/bun') }} + changed_browser_integration: + ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, + '@sentry-internal/browser-integration-tests') }} job_check_branches: name: Check PR branches @@ -263,8 +276,6 @@ jobs: dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Lint source files run: yarn lint:lerna - - name: Lint C++ files - run: yarn lint:clang - name: Lint for ES compatibility run: yarn lint:es-compatibility @@ -289,7 +300,7 @@ jobs: id: install_dependencies - name: Check file formatting - run: yarn lint:prettier && yarn lint:biome + run: yarn lint:prettier job_circular_dep_check: name: Circular Dependency Check @@ -439,7 +450,7 @@ jobs: with: node-version-file: 'package.json' - name: Set up Deno - uses: denoland/setup-deno@v2.0.1 + uses: denoland/setup-deno@v2.0.2 with: deno-version: v2.1.5 - name: Restore caches @@ -506,7 +517,9 @@ jobs: token: ${{ secrets.CODECOV_TOKEN }} job_browser_playwright_tests: - name: Playwright ${{ matrix.bundle }}${{ matrix.project && matrix.project != 'chromium' && format(' {0}', matrix.project) || ''}}${{ matrix.shard && format(' ({0}/{1})', matrix.shard, matrix.shards) || ''}} Tests + name: + Playwright ${{ matrix.bundle }}${{ matrix.project && matrix.project != 'chromium' && format(' {0}', + matrix.project) || ''}}${{ matrix.shard && format(' ({0}/{1})', matrix.shard, matrix.shards) || ''}} Tests needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' runs-on: ubuntu-20.04-large-js @@ -576,13 +589,17 @@ jobs: env: PW_BUNDLE: ${{ matrix.bundle }} working-directory: dev-packages/browser-integration-tests - run: yarn test:all${{ matrix.project && format(' --project={0}', matrix.project) || '' }}${{ matrix.shard && format(' --shard={0}/{1}', matrix.shard, matrix.shards) || '' }} + run: + yarn test:all${{ matrix.project && format(' --project={0}', matrix.project) || '' }}${{ matrix.shard && + format(' --shard={0}/{1}', matrix.shard, matrix.shards) || '' }} - name: Upload Playwright Traces uses: actions/upload-artifact@v4 if: failure() with: - name: playwright-traces-job_browser_playwright_tests-${{ matrix.bundle}}-${{matrix.project}}-${{matrix.shard || '0'}} + name: + playwright-traces-job_browser_playwright_tests-${{ matrix.bundle}}-${{matrix.project}}-${{matrix.shard || + '0'}} path: dev-packages/browser-integration-tests/test-results overwrite: true retention-days: 7 @@ -824,21 +841,26 @@ jobs: - name: Determine which E2E test applications should be run id: matrix - run: yarn --silent ci:build-matrix --base=${{ (github.event_name == 'pull_request' && github.event.pull_request.base.sha) || '' }} >> $GITHUB_OUTPUT + run: + yarn --silent ci:build-matrix --base=${{ (github.event_name == 'pull_request' && + github.event.pull_request.base.sha) || '' }} >> $GITHUB_OUTPUT working-directory: dev-packages/e2e-tests - name: Determine which optional E2E test applications should be run id: matrix-optional - run: yarn --silent ci:build-matrix-optional --base=${{ (github.event_name == 'pull_request' && github.event.pull_request.base.sha) || '' }} >> $GITHUB_OUTPUT + run: + yarn --silent ci:build-matrix-optional --base=${{ (github.event_name == 'pull_request' && + github.event.pull_request.base.sha) || '' }} >> $GITHUB_OUTPUT working-directory: dev-packages/e2e-tests job_e2e_tests: name: E2E ${{ matrix.label || matrix.test-application }} Test # We need to add the `always()` check here because the previous step has this as well :( # See: https://github.com/actions/runner/issues/2205 - if: always() && needs.job_e2e_prepare.result == 'success' && needs.job_e2e_prepare.outputs.matrix != '{"include":[]}' + if: + always() && needs.job_e2e_prepare.result == 'success' && needs.job_e2e_prepare.outputs.matrix != '{"include":[]}' needs: [job_get_metadata, job_build, job_e2e_prepare] - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 timeout-minutes: 15 env: # We just use a dummy DSN here, only send to the tunnel anyhow @@ -953,12 +975,9 @@ jobs: # We need to add the `always()` check here because the previous step has this as well :( # See: https://github.com/actions/runner/issues/2205 if: - always() && - needs.job_get_metadata.outputs.is_release != 'true' && - needs.job_e2e_prepare.result == 'success' && - needs.job_e2e_prepare.outputs.matrix-optional != '{"include":[]}' && - (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && - github.actor != 'dependabot[bot]' + always() && needs.job_get_metadata.outputs.is_release != 'true' && needs.job_e2e_prepare.result == 'success' && + needs.job_e2e_prepare.outputs.matrix-optional != '{"include":[]}' && (github.event_name != 'pull_request' || + github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' needs: [job_get_metadata, job_build, job_e2e_prepare] runs-on: ubuntu-20.04 timeout-minutes: 15 diff --git a/.github/workflows/cleanup-pr-caches.yml b/.github/workflows/cleanup-pr-caches.yml index 5ebd0b7aad9d..591d9292e0c0 100644 --- a/.github/workflows/cleanup-pr-caches.yml +++ b/.github/workflows/cleanup-pr-caches.yml @@ -1,4 +1,4 @@ -name: "Automation: Cleanup PR caches" +name: 'Automation: Cleanup PR caches' on: pull_request: types: diff --git a/.github/workflows/clear-cache.yml b/.github/workflows/clear-cache.yml index 5c327553e3b8..3109b0d49ff3 100644 --- a/.github/workflows/clear-cache.yml +++ b/.github/workflows/clear-cache.yml @@ -1,4 +1,4 @@ -name: "Action: Clear all GHA caches" +name: 'Action: Clear all GHA caches' on: workflow_dispatch: inputs: diff --git a/.github/workflows/enforce-license-compliance.yml b/.github/workflows/enforce-license-compliance.yml index 776f8135178d..c8944467384b 100644 --- a/.github/workflows/enforce-license-compliance.yml +++ b/.github/workflows/enforce-license-compliance.yml @@ -1,4 +1,4 @@ -name: "CI: Enforce License Compliance" +name: 'CI: Enforce License Compliance' on: push: diff --git a/.github/workflows/external-contributors.yml b/.github/workflows/external-contributors.yml index e9b1e05a2c92..0ba6f0681f4e 100644 --- a/.github/workflows/external-contributors.yml +++ b/.github/workflows/external-contributors.yml @@ -1,4 +1,4 @@ -name: "CI: Mention external contributors" +name: 'CI: Mention external contributors' on: pull_request_target: types: @@ -36,15 +36,16 @@ jobs: author_association: ${{ github.event.pull_request.author_association }} - name: Create PR with changes - uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f + uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f with: # This token is scoped to Daniel Griesser # If we used the default GITHUB_TOKEN, the resulting PR would not trigger CI :( token: ${{ secrets.REPO_SCOPED_TOKEN }} - commit-message: "chore: Add external contributor to CHANGELOG.md" - title: "chore: Add external contributor to CHANGELOG.md" + commit-message: 'chore: Add external contributor to CHANGELOG.md' + title: 'chore: Add external contributor to CHANGELOG.md' branch: 'external-contributor/patch-${{ github.event.pull_request.user.login }}' base: 'develop' delete-branch: true - body: "This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their contribution. See #${{ github.event.pull_request.number }}" - + body: + 'This PR adds the external contributor to the CHANGELOG.md file, so that they are credited for their + contribution. See #${{ github.event.pull_request.number }}' diff --git a/.github/workflows/gitflow-sync-develop.yml b/.github/workflows/gitflow-sync-develop.yml index 893dbbbf56fb..5b6f22f27682 100644 --- a/.github/workflows/gitflow-sync-develop.yml +++ b/.github/workflows/gitflow-sync-develop.yml @@ -1,4 +1,4 @@ -name: "Gitflow: Sync master into develop" +name: 'Gitflow: Sync master into develop' on: push: branches: diff --git a/.github/workflows/project-automation.yml b/.github/workflows/project-automation.yml index ce57e279dcf5..bf34f2e6078f 100644 --- a/.github/workflows/project-automation.yml +++ b/.github/workflows/project-automation.yml @@ -1,4 +1,4 @@ -name: "Automation: Update GH Project" +name: 'Automation: Update GH Project' on: pull_request: types: @@ -14,29 +14,29 @@ jobs: name: Check if PR is in project runs-on: ubuntu-latest steps: - - name: Check if PR is in project - continue-on-error: true - id: check_project - uses: github/update-project-action@f980378bc179626af5b4e20ec05ec39c7f7a6f6d - with: - github_token: ${{ secrets.GH_PROJECT_AUTOMATION }} - organization: getsentry - project_number: 31 - content_id: ${{ github.event.pull_request.node_id }} - field: Status - operation: read + - name: Check if PR is in project + continue-on-error: true + id: check_project + uses: github/update-project-action@f980378bc179626af5b4e20ec05ec39c7f7a6f6d + with: + github_token: ${{ secrets.GH_PROJECT_AUTOMATION }} + organization: getsentry + project_number: 31 + content_id: ${{ github.event.pull_request.node_id }} + field: Status + operation: read - - name: If project field is read, set is_in_project to 1 - if: steps.check_project.outputs.field_read_value - id: is_in_project - run: echo "is_in_project=1" >> "$GITHUB_OUTPUT" + - name: If project field is read, set is_in_project to 1 + if: steps.check_project.outputs.field_read_value + id: is_in_project + run: echo "is_in_project=1" >> "$GITHUB_OUTPUT" outputs: is_in_project: ${{ steps.is_in_project.outputs.is_in_project || '0' }} # When a PR is a draft, it should go into "In Progress" mark_as_in_progress: - name: "Mark as In Progress" + name: 'Mark as In Progress' needs: check_project if: | needs.check_project.outputs.is_in_project == '1' @@ -52,11 +52,11 @@ jobs: project_number: 31 content_id: ${{ github.event.pull_request.node_id }} field: Status - value: "🏗 In Progress" + value: '🏗 In Progress' # When a PR is not a draft, it should go into "In Review" mark_as_in_review: - name: "Mark as In Review" + name: 'Mark as In Review' needs: check_project if: | needs.check_project.outputs.is_in_project == '1' @@ -73,12 +73,12 @@ jobs: project_number: 31 content_id: ${{ github.event.pull_request.node_id }} field: Status - value: "👀 In Review" + value: '👀 In Review' # By default, closed PRs go into "Ready for Release" # But if they are closed without merging, they should go into "Done" mark_as_done: - name: "Mark as Done" + name: 'Mark as Done' needs: check_project if: | needs.check_project.outputs.is_in_project == '1' @@ -94,5 +94,4 @@ jobs: project_number: 31 content_id: ${{ github.event.pull_request.node_id }} field: Status - value: "✅ Done" - + value: '✅ Done' diff --git a/.github/workflows/release-comment-issues.yml b/.github/workflows/release-comment-issues.yml index 4bbcb29aba21..9b1a1f849ab3 100644 --- a/.github/workflows/release-comment-issues.yml +++ b/.github/workflows/release-comment-issues.yml @@ -1,4 +1,4 @@ -name: "Automation: Notify issues for release" +name: 'Automation: Notify issues for release' on: release: types: @@ -17,7 +17,10 @@ jobs: steps: - name: Get version id: get_version - run: echo "version=${{ github.event.inputs.version || github.event.release.tag_name }}" >> $GITHUB_OUTPUT + env: + INPUTS_VERSION: ${{ github.event.inputs.version }} + RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} + run: echo "version=${INPUTS_VERSION:-$RELEASE_TAG_NAME}" >> "$GITHUB_OUTPUT" - name: Comment on linked issues that are mentioned in release if: | diff --git a/.github/workflows/release-size-info.yml b/.github/workflows/release-size-info.yml index 04e51e5ae14e..70a1ecfda2f0 100644 --- a/.github/workflows/release-size-info.yml +++ b/.github/workflows/release-size-info.yml @@ -1,4 +1,4 @@ -name: "Automation: Add size info to release" +name: 'Automation: Add size info to release' on: release: types: @@ -19,7 +19,10 @@ jobs: steps: - name: Get version id: get_version - run: echo "version=${{ github.event.inputs.version || github.event.release.tag_name }}" >> $GITHUB_OUTPUT + env: + INPUTS_VERSION: ${{ github.event.inputs.version }} + RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} + run: echo "version=${INPUTS_VERSION:-$RELEASE_TAG_NAME}" >> "$GITHUB_OUTPUT" - name: Update Github Release if: steps.get_version.outputs.version != '' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1e95378de74..9028ce552447 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,4 @@ -name: "Action: Prepare Release" +name: 'Action: Prepare Release' on: workflow_dispatch: inputs: @@ -40,4 +40,3 @@ jobs: force: ${{ github.event.inputs.force }} merge_target: ${{ github.event.inputs.merge_target }} craft_config_from_merge_target: true - diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000000..cb7b3db12641 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +packages/browser/test/loader.js +packages/replay-worker/examples/worker.min.js diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000000..ba9a3dc2c246 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "arrowParens": "avoid", + "printWidth": 120, + "proseWrap": "always", + "singleQuote": true, + "trailingComma": "all" +} diff --git a/.size-limit.js b/.size-limit.js index 08adf5a80c29..ffa69d850947 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -47,7 +47,7 @@ module.exports = [ path: 'packages/browser/build/npm/esm/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, - limit: '75 KB', + limit: '75.2 KB', }, { name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags', diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 9b4a2aa7eb87..3ad96b1733d5 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -3,7 +3,6 @@ // for the documentation about the extensions.json format "recommendations": [ "esbenp.prettier-vscode", - "biomejs.biome", "dbaeumer.vscode-eslint", "augustocdias.tasks-shell-input", "denoland.vscode-deno" diff --git a/.vscode/settings.json b/.vscode/settings.json index 0f2399922cfc..c3515b80ced8 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,8 +25,8 @@ } ], "deno.enablePaths": ["packages/deno/test"], - "editor.defaultFormatter": "biomejs.biome", + "editor.defaultFormatter": "esbenp.prettier-vscode", "[typescript]": { - "editor.defaultFormatter": "biomejs.biome" + "editor.defaultFormatter": "esbenp.prettier-vscode" } } diff --git a/CHANGELOG.md b/CHANGELOG.md index bfdaee843a0c..116621720966 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,42 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 9.3.0 + +### Important Changes + +With this release we're publishing two new SDKs in **experimental alpha** stage: + +- **feat(tanstackstart): Add TanStack Start SDK ([#15523](https://github.com/getsentry/sentry-javascript/pull/15523))** + +For details please refer to the [README](https://github.com/getsentry/sentry-javascript/tree/develop/packages/tanstackstart) + +- **feat(react-router): Add React Router SDK ([#15524](https://github.com/getsentry/sentry-javascript/pull/15524))** + +For details please refer to the [README](https://github.com/getsentry/sentry-javascript/tree/develop/packages/react-router) + +- **feat(remix): Add support for Hydrogen ([#15450](https://github.com/getsentry/sentry-javascript/pull/15450))** + +This PR adds support for Shopify Hydrogen applications running on MiniOxygen runtime. + +### Other Changes + +- feat(core): Add `forceTransaction` to trpc middleware options ([#15519](https://github.com/getsentry/sentry-javascript/pull/15519)) +- feat(core): Default filter unactionable error ([#15527](https://github.com/getsentry/sentry-javascript/pull/15527)) +- feat(core): Rename `inboundFiltersIntegration` to `eventFiltersIntegration` ([#15434](https://github.com/getsentry/sentry-javascript/pull/15434)) +- feat(deps): bump @prisma/instrumentation from 6.2.1 to 6.4.1 ([#15480](https://github.com/getsentry/sentry-javascript/pull/15480)) +- feat(react-router): Add build-time config ([#15406](https://github.com/getsentry/sentry-javascript/pull/15406)) +- feat(replay): Bump rrweb to 2.33.0 ([#15514](https://github.com/getsentry/sentry-javascript/pull/15514)) +- fix(core): Fix `allowUrls` and `denyUrls` for linked and aggregate exceptions ([#15521](https://github.com/getsentry/sentry-javascript/pull/15521)) +- fix(nextjs): Don't capture devmode server-action redirect errors ([#15485](https://github.com/getsentry/sentry-javascript/pull/15485)) +- fix(nextjs): warn about missing onRequestError handler [#15488](https://github.com/getsentry/sentry-javascript/pull/15488)) +- fix(nextjs): Prevent wrong culprit from showing up for clientside error events [#15475](https://github.com/getsentry/sentry-javascript/pull/15475)) +- fix(nuxt): Ignore 300-400 status codes on app errors in Nuxt ([#15473](https://github.com/getsentry/sentry-javascript/pull/15473)) +- fix(react): Add support for cross-usage of React Router instrumentations ([#15283](https://github.com/getsentry/sentry-javascript/pull/15283)) +- fix(sveltekit): Guard `process` check when flushing events ([#15516](https://github.com/getsentry/sentry-javascript/pull/15516)) + +Work in this release was contributed by @GerryWilko and @leoambio. Thank you for your contributions! + ## 9.2.0 ### Important Changes diff --git a/biome.json b/biome.json deleted file mode 100644 index 010139fbaa82..000000000000 --- a/biome.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", - "vcs": { - "enabled": true, - "clientKind": "git", - "useIgnoreFile": true, - "defaultBranch": "develop" - }, - "organizeImports": { - "enabled": true - }, - "linter": { - "enabled": true, - "rules": { - "recommended": false, - "correctness": { - "all": false, - "noUnusedVariables": "error", - "noPrecisionLoss": "error" - }, - "complexity": { - "useRegexLiterals": "error" - }, - "suspicious": { - "all": false, - "noControlCharactersInRegex": "error" - }, - "nursery": { - "noUnusedImports": "error" - }, - "performance": { - "all": true, - "noAccumulatingSpread": "error", - "noDelete": "off" - } - }, - "ignore": [ - ".vscode", - "**/*.json", - "**/fixtures/*/*.json", - "**/*.min.js", - ".next", - ".nuxt", - ".svelte-kit", - ".angular", - "angular.json", - "ember/instance-initializers", - "ember/types.d.ts", - "solidstart/*.d.ts", - "solidstart/client/", - "solidstart/server/", - ".output", - ".vinxi" - ] - }, - "files": { - "ignoreUnknown": true - }, - "formatter": { - "enabled": true, - "formatWithErrors": true, - "indentStyle": "space", - "indentWidth": 2, - "lineWidth": 120, - "ignore": [ - "dev-packages/browser-integration-tests/fixtures/loader.js", - "dev-packages/browser-integration-tests/suites/**/*.json", - "dev-packages/browser-integration-tests/loader-suites/**/*.js", - "dev-packages/browser-integration-tests/suites/stacktraces/**/*.js", - ".next/**/*", - "**/fixtures/*/*.json", - "**/*.min.js", - ".next/**", - ".svelte-kit/**", - ".angular/**", - "angular.json", - "**/profiling-node/lib/**", - "ember/instance-initializers/**", - "ember/types.d.ts" - ] - }, - "javascript": { - "formatter": { - "enabled": true, - "quoteStyle": "single", - "arrowParentheses": "asNeeded", - "trailingComma": "all", - "lineEnding": "lf" - }, - "parser": { - "unsafeParameterDecoratorsEnabled": true - } - }, - "json": { - "formatter": { - "enabled": true - }, - "parser": { - "allowComments": true, - "allowTrailingCommas": true - } - } -} diff --git a/codecov.yml b/codecov.yml index 1013e1b11e24..f8c0cbc17ba5 100644 --- a/codecov.yml +++ b/codecov.yml @@ -5,8 +5,8 @@ codecov: ai_pr_review: enabled: true - method: "label" - label_name: "ci-codecov-ai-review" + method: 'label' + label_name: 'ci-codecov-ai-review' coverage: precision: 2 diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index fff2afc9cc45..275312412045 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -41,7 +41,7 @@ "dependencies": { "@babel/preset-typescript": "^7.16.7", "@playwright/test": "~1.50.0", - "@sentry-internal/rrweb": "2.31.0", + "@sentry-internal/rrweb": "2.33.0", "@sentry/browser": "9.2.0", "axios": "1.7.7", "babel-loader": "^8.2.2", diff --git a/dev-packages/browser-integration-tests/suites/manual-client/browser-context/init.js b/dev-packages/browser-integration-tests/suites/manual-client/browser-context/init.js index 64e5af159cb0..a02b77c89b49 100644 --- a/dev-packages/browser-integration-tests/suites/manual-client/browser-context/init.js +++ b/dev-packages/browser-integration-tests/suites/manual-client/browser-context/init.js @@ -6,7 +6,7 @@ import { defaultStackParser, functionToStringIntegration, httpContextIntegration, - inboundFiltersIntegration, + eventFiltersIntegration, linkedErrorsIntegration, makeFetchTransport, } from '@sentry/browser'; @@ -16,7 +16,7 @@ const integrations = [ functionToStringIntegration(), dedupeIntegration(), httpContextIntegration(), - inboundFiltersIntegration(), + eventFiltersIntegration(), linkedErrorsIntegration(), ]; diff --git a/dev-packages/browser-integration-tests/suites/manual-client/browser-context/test.ts b/dev-packages/browser-integration-tests/suites/manual-client/browser-context/test.ts index 642006f4c2fc..e77b0cfd3729 100644 --- a/dev-packages/browser-integration-tests/suites/manual-client/browser-context/test.ts +++ b/dev-packages/browser-integration-tests/suites/manual-client/browser-context/test.ts @@ -38,7 +38,7 @@ sentryTest('allows to setup a client manually & capture exceptions', async ({ ge environment: 'local', release: '0.0.1', sdk: { - integrations: ['Breadcrumbs', 'FunctionToString', 'Dedupe', 'HttpContext', 'InboundFilters', 'LinkedErrors'], + integrations: ['Breadcrumbs', 'FunctionToString', 'Dedupe', 'HttpContext', 'EventFilters', 'LinkedErrors'], name: 'sentry.javascript.browser', version: expect.any(String), packages: [{ name: expect.any(String), version: expect.any(String) }], diff --git a/dev-packages/clear-cache-gh-action/action.yml b/dev-packages/clear-cache-gh-action/action.yml index 06493534b23e..739b74294470 100644 --- a/dev-packages/clear-cache-gh-action/action.yml +++ b/dev-packages/clear-cache-gh-action/action.yml @@ -6,19 +6,19 @@ inputs: description: 'a github access token' clear_develop: required: false - default: "" - description: "If set, also clear caches from develop branch." + default: '' + description: 'If set, also clear caches from develop branch.' clear_branches: required: false - default: "" - description: "If set, also clear caches from non-develop branches." + default: '' + description: 'If set, also clear caches from non-develop branches.' clear_pending_prs: required: false - default: "" - description: "If set, also clear caches from pending PR workflow runs." + default: '' + description: 'If set, also clear caches from pending PR workflow runs.' workflow_name: required: false - default: "CI: Build & Test" + default: 'CI: Build & Test' description: The workflow to clear caches for. runs: using: 'node20' diff --git a/dev-packages/e2e-tests/test-applications/cloudflare-workers/package.json b/dev-packages/e2e-tests/test-applications/cloudflare-workers/package.json index f7fd08df85f9..e926cc164121 100644 --- a/dev-packages/e2e-tests/test-applications/cloudflare-workers/package.json +++ b/dev-packages/e2e-tests/test-applications/cloudflare-workers/package.json @@ -19,7 +19,7 @@ "@cloudflare/vitest-pool-workers": "^0.4.5", "@cloudflare/workers-types": "^4.20240725.0", "typescript": "^5.5.2", - "vitest": "1.5.0", + "vitest": "1.6.1", "wrangler": "^3.60.3" }, "volta": { diff --git a/dev-packages/e2e-tests/test-applications/generic-ts3.8/index.ts b/dev-packages/e2e-tests/test-applications/generic-ts3.8/index.ts index 03cebd495539..beb10260da38 100644 --- a/dev-packages/e2e-tests/test-applications/generic-ts3.8/index.ts +++ b/dev-packages/e2e-tests/test-applications/generic-ts3.8/index.ts @@ -1,10 +1,5 @@ -// biome-ignore lint/nursery/noUnusedImports: import * as _SentryReplay from '@sentry-internal/replay'; -// biome-ignore lint/nursery/noUnusedImports: we need to import the SDK to ensure tsc check the types import * as _SentryBrowser from '@sentry/browser'; -// biome-ignore lint/nursery/noUnusedImports: import * as _SentryCore from '@sentry/core'; -// biome-ignore lint/nursery/noUnusedImports: import * as _SentryNode from '@sentry/node'; -// biome-ignore lint/nursery/noUnusedImports: import * as _SentryWasm from '@sentry/wasm'; diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-action/page.tsx b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-action/page.tsx index 18ffe80fe707..4cbeafba4f67 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-action/page.tsx +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/app/server-action/page.tsx @@ -1,6 +1,6 @@ import * as Sentry from '@sentry/nextjs'; import { headers } from 'next/headers'; -import { notFound } from 'next/navigation'; +import { notFound, redirect } from 'next/navigation'; export default function ServerComponent() { async function myServerAction(formData: FormData) { @@ -26,6 +26,17 @@ export default function ServerComponent() { ); } + async function redirectServerAction(formData: FormData) { + 'use server'; + return await Sentry.withServerActionInstrumentation( + 'redirectServerAction', + { formData, headers: headers(), recordResponse: true }, + () => { + redirect('/'); + }, + ); + } + return ( <> {/* @ts-ignore */} @@ -38,6 +49,11 @@ export default function ServerComponent() { + {/* @ts-ignore */} +
+ + +
); } diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts index 5656ce0e5e57..3ad9e43f06db 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts @@ -1,5 +1,5 @@ import { expect, test } from '@playwright/test'; -import { waitForTransaction } from '@sentry-internal/test-utils'; +import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; const packageJson = require('../package.json'); @@ -108,19 +108,49 @@ test('Should set not_found status for server actions calling notFound()', async const nextjsMajor = Number(nextjsVersion.split('.')[0]); test.skip(!isNaN(nextjsMajor) && nextjsMajor < 14, 'only applies to nextjs apps >= version 14'); - const serverComponentTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => { + const serverActionTransactionPromise = waitForTransaction('nextjs-app-dir', async transactionEvent => { return transactionEvent?.transaction === 'serverAction/notFoundServerAction'; }); await page.goto('/server-action'); await page.getByText('Run NotFound Action').click(); - const transactionEvent = await serverComponentTransactionPromise; + const transactionEvent = await serverActionTransactionPromise; expect(transactionEvent).toBeDefined(); expect(transactionEvent.contexts?.trace?.status).toBe('not_found'); }); +test('Should not capture "NEXT_REDIRECT" control-flow errors for server actions calling redirect()', async ({ + page, +}) => { + const nextjsVersion = packageJson.dependencies.next; + const nextjsMajor = Number(nextjsVersion.split('.')[0]); + test.skip(!isNaN(nextjsMajor) && nextjsMajor < 14, 'only applies to nextjs apps >= version 14'); + + const serverActionTransactionPromise = waitForTransaction('nextjs-app-dir', transactionEvent => { + return transactionEvent?.transaction === 'serverAction/redirectServerAction'; + }); + + let controlFlowErrorCaptured = false; + waitForError('nextjs-app-dir', errorEvent => { + if (errorEvent.exception?.values?.[0].value === 'NEXT_REDIRECT') { + controlFlowErrorCaptured = true; + } + + return false; + }); + + await page.goto('/server-action'); + await page.getByText('Run Redirect Action').click(); + + const serverActionTransactionEvent = await serverActionTransactionPromise; + expect(serverActionTransactionEvent).toBeDefined(); + + // By the time the server action span finishes the error should already have been sent + expect(controlFlowErrorCaptured).toBe(false); +}); + test('Will not include spans in pageload transaction with faulty timestamps for slow loading pages', async ({ page, }) => { diff --git a/dev-packages/e2e-tests/test-applications/react-17/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-17/src/pages/Index.tsx index c7aa909c3c6e..754af82320e9 100644 --- a/dev-packages/e2e-tests/test-applications/react-17/src/pages/Index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-17/src/pages/Index.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Link } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-17/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-17/src/pages/User.tsx index 62f0c2d17533..671455a92fff 100644 --- a/dev-packages/e2e-tests/test-applications/react-17/src/pages/User.tsx +++ b/dev-packages/e2e-tests/test-applications/react-17/src/pages/User.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const User = () => { diff --git a/dev-packages/e2e-tests/test-applications/react-19/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-19/src/index.tsx index 6b721b0161f0..16209793ae07 100644 --- a/dev-packages/e2e-tests/test-applications/react-19/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-19/src/index.tsx @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/react'; -// biome-ignore lint/nursery/noUnusedImports: import React from 'react'; import ReactDOM from 'react-dom/client'; import Index from './pages/Index'; diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/Index.tsx index 12bfb12ec3a9..a34c785e2b82 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/Index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/Index.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Link } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx index 1410df69124b..915cb720974a 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/LazyLoadedInnerRoute.tsx @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/react'; -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Route, Routes } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/User.tsx index 62f0c2d17533..671455a92fff 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/User.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-browser-router/src/pages/User.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const User = () => { diff --git a/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/pages/Index.tsx index d6b71a1d1279..1000dd53df27 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/pages/Index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/pages/Index.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Link } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/pages/User.tsx index 62f0c2d17533..671455a92fff 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/pages/User.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-hash-router/src/pages/User.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const User = () => { diff --git a/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/pages/Index.tsx index b025f721e100..85b28c5a7dbd 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/pages/Index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/pages/Index.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const Index = () => { diff --git a/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/pages/User.tsx index e54d6c604e2d..266bc5b5bb06 100644 --- a/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/pages/User.tsx +++ b/dev-packages/e2e-tests/test-applications/react-create-memory-router/src/pages/User.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Link } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-router-5/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-5/src/index.tsx index 315ba07ad8c4..cc9b307e620a 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-5/src/index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-5/src/index.tsx @@ -1,6 +1,5 @@ import * as Sentry from '@sentry/react'; import { createBrowserHistory } from 'history'; -// biome-ignore lint/nursery/noUnusedImports: import React from 'react'; import ReactDOM from 'react-dom/client'; import { Route, Router, Switch } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-router-5/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-router-5/src/pages/Index.tsx index 7789a2773224..f339eb867d6c 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-5/src/pages/Index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-5/src/pages/Index.tsx @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/react'; -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Link } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-router-5/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-router-5/src/pages/User.tsx index 3b41552d35d3..c4aad7c7842c 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-5/src/pages/User.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-5/src/pages/User.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const User = (params: { id: string }) => { diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/src/pages/Index.tsx index d2362c149f84..f0dc55c761a6 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/src/pages/Index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-6-descendant-routes/src/pages/Index.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Link } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/pages/Index.tsx index 7789a2773224..f339eb867d6c 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/pages/Index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/pages/Index.tsx @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/react'; -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Link } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/pages/User.tsx index 62f0c2d17533..671455a92fff 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/pages/User.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-6-use-routes/src/pages/User.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const User = () => { diff --git a/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/Index.tsx index d6b71a1d1279..1000dd53df27 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/Index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/Index.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Link } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/SSE.tsx b/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/SSE.tsx index 64a9f5717114..4c0ae97036ad 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/SSE.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/SSE.tsx @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/react'; -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const fetchSSE = async ({ timeout, abort = false }: { timeout: boolean; abort?: boolean }) => { diff --git a/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/User.tsx index 62f0c2d17533..671455a92fff 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/User.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-6/src/pages/User.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const User = () => { diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/.gitignore b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/.gitignore new file mode 100644 index 000000000000..84634c973eeb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/.gitignore @@ -0,0 +1,29 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# production +/build + +# misc +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +/test-results/ +/playwright-report/ +/playwright/.cache/ + +!*.d.ts diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/.npmrc b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/package.json b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/package.json new file mode 100644 index 000000000000..b86327f94772 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/package.json @@ -0,0 +1,53 @@ +{ + "name": "react-router-7-cross-usage", + "version": "0.1.0", + "private": true, + "dependencies": { + "@sentry/react": "latest || *", + "@types/react": "18.0.0", + "@types/react-dom": "18.0.0", + "express": "4.20.0", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-router-dom": "^7.2.0", + "react-scripts": "5.0.1", + "typescript": "~5.0.0" + }, + "scripts": { + "build": "react-scripts build", + "start": "serve -s build", + "test": "playwright test", + "clean": "npx rimraf node_modules pnpm-lock.yaml", + "test:build": "pnpm install && npx playwright install && pnpm build", + "test:build-ts3.8": "pnpm install && pnpm add typescript@3.8 && npx playwright install && pnpm build", + "test:build-canary": "pnpm install && pnpm add react@canary react-dom@canary && npx playwright install && pnpm build", + "test:assert": "pnpm test" + }, + "eslintConfig": { + "extends": [ + "react-app", + "react-app/jest" + ] + }, + "browserslist": { + "production": [ + ">0.2%", + "not dead", + "not op_mini all" + ], + "development": [ + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" + ] + }, + "devDependencies": { + "@playwright/test": "~1.50.0", + "@sentry-internal/test-utils": "link:../../../test-utils", + "serve": "14.0.1", + "npm-run-all2": "^6.2.0" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/playwright.config.mjs new file mode 100644 index 000000000000..31f2b913b58b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/playwright.config.mjs @@ -0,0 +1,7 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: `pnpm start`, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/public/index.html b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/public/index.html new file mode 100644 index 000000000000..39da76522bea --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/public/index.html @@ -0,0 +1,24 @@ + + + + + + + + React App + + + +
+ + + diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/globals.d.ts b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/globals.d.ts new file mode 100644 index 000000000000..ffa61ca49acc --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/globals.d.ts @@ -0,0 +1,5 @@ +interface Window { + recordedTransactions?: string[]; + capturedExceptionId?: string; + sentryReplayId?: string; +} diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx new file mode 100644 index 000000000000..bfcc527ded1b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/index.tsx @@ -0,0 +1,107 @@ +import * as Sentry from '@sentry/react'; +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { + Outlet, + Route, + RouterProvider, + Routes, + createBrowserRouter, + createRoutesFromChildren, + matchRoutes, + useLocation, + useNavigationType, + useRoutes, +} from 'react-router-dom'; +import Index from './pages/Index'; + +const replay = Sentry.replayIntegration(); + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: process.env.REACT_APP_E2E_TEST_DSN, + integrations: [ + Sentry.reactRouterV7BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + trackFetchStreamPerformance: true, + }), + replay, + ], + // We recommend adjusting this value in production, or using tracesSampler + // for finer control + tracesSampleRate: 1.0, + release: 'e2e-test', + + // Always capture replays, so we can test this properly + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 0.0, + + tunnel: 'http://localhost:3031', +}); + +const SentryRoutes = Sentry.withSentryReactRouterV7Routing(Routes); +const sentryUseRoutes = Sentry.wrapUseRoutesV7(useRoutes); +const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(createBrowserRouter); + +const DetailsRoutes = () => + sentryUseRoutes([ + { + path: ':detailId', + element:
Details
, + }, + ]); + +const DetailsRoutesAlternative = () => ( + + Details} /> + +); + +const ViewsRoutes = () => + sentryUseRoutes([ + { + index: true, + element:
Views
, + }, + { + path: 'views/:viewId/*', + element: , + }, + { + path: 'old-views/:viewId/*', + element: , + }, + ]); + +const ProjectsRoutes = () => ( + + }> + Project Page Root} /> + }> + } /> + + + +); + +const router = sentryCreateBrowserRouter([ + { + children: [ + { + path: '/', + element: , + }, + { + path: '/*', + element: , + }, + ], + }, +]); + +const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement); +root.render(); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/pages/Index.tsx new file mode 100644 index 000000000000..f0dc55c761a6 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/pages/Index.tsx @@ -0,0 +1,17 @@ +import * as React from 'react'; +import { Link } from 'react-router-dom'; + +const Index = () => { + return ( + <> + + navigate + + + navigate old + + + ); +}; + +export default Index; diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/react-app-env.d.ts b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/react-app-env.d.ts new file mode 100644 index 000000000000..6431bc5fc6b2 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/src/react-app-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/start-event-proxy.mjs new file mode 100644 index 000000000000..0b60f17c3266 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'react-router-7-cross-usage', +}); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts new file mode 100644 index 000000000000..1b521964f770 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tests/transactions.test.ts @@ -0,0 +1,138 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test('sends a pageload transaction with a parameterized URL', async ({ page }) => { + const transactionPromise = waitForTransaction('react-router-7-cross-usage', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + await page.goto(`/projects/123/views/234/567`); + + const rootSpan = await transactionPromise; + + expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + expect(rootSpan).toMatchObject({ + contexts: { + trace: { + op: 'pageload', + origin: 'auto.pageload.react.reactrouter_v7', + }, + }, + transaction: '/projects/:projectId/views/:viewId/:detailId', + transaction_info: { + source: 'route', + }, + }); +}); + +test('sends a pageload transaction with a parameterized URL - alternative route', async ({ page }) => { + const transactionPromise = waitForTransaction('react-router-7-cross-usage', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + await page.goto(`/projects/234/old-views/234/567`); + + const rootSpan = await transactionPromise; + + expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + expect(rootSpan).toMatchObject({ + contexts: { + trace: { + op: 'pageload', + origin: 'auto.pageload.react.reactrouter_v7', + }, + }, + transaction: '/projects/:projectId/old-views/:viewId/:detailId', + transaction_info: { + source: 'route', + }, + }); +}); + +test('sends a navigation transaction with a parameterized URL', async ({ page }) => { + const pageloadTxnPromise = waitForTransaction('react-router-7-cross-usage', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + const navigationTxnPromise = waitForTransaction('react-router-7-cross-usage', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation'; + }); + + await page.goto(`/`); + const pageloadTxn = await pageloadTxnPromise; + + expect(pageloadTxn).toMatchObject({ + contexts: { + trace: { + op: 'pageload', + origin: 'auto.pageload.react.reactrouter_v7', + }, + }, + transaction: '/', + transaction_info: { + source: 'route', + }, + }); + + const linkElement = page.locator('id=navigation'); + + const [_, navigationTxn] = await Promise.all([linkElement.click(), navigationTxnPromise]); + + expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + expect(navigationTxn).toMatchObject({ + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.react.reactrouter_v7', + }, + }, + transaction: '/projects/:projectId/views/:viewId/:detailId', + transaction_info: { + source: 'route', + }, + }); +}); + +test('sends a navigation transaction with a parameterized URL - alternative route', async ({ page }) => { + const pageloadTxnPromise = waitForTransaction('react-router-7-cross-usage', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + const navigationTxnPromise = waitForTransaction('react-router-7-cross-usage', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation'; + }); + + await page.goto(`/`); + const pageloadTxn = await pageloadTxnPromise; + + expect(pageloadTxn).toMatchObject({ + contexts: { + trace: { + op: 'pageload', + origin: 'auto.pageload.react.reactrouter_v7', + }, + }, + transaction: '/', + transaction_info: { + source: 'route', + }, + }); + + const linkElement = page.locator('id=old-navigation'); + + const [_, navigationTxn] = await Promise.all([linkElement.click(), navigationTxnPromise]); + + expect((await page.innerHTML('#root')).includes('Details')).toBe(true); + expect(navigationTxn).toMatchObject({ + contexts: { + trace: { + op: 'navigation', + origin: 'auto.navigation.react.reactrouter_v7', + }, + }, + transaction: '/projects/:projectId/old-views/:viewId/:detailId', + transaction_info: { + source: 'route', + }, + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tsconfig.json b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tsconfig.json new file mode 100644 index 000000000000..4cc95dc2689a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/react-router-7-cross-usage/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "target": "es2018", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react" + }, + "include": ["src", "tests"] +} diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/Index.tsx index 3f660ed4f4f7..688cba53fb70 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/Index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/Index.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Link } from 'react-router'; diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/SSE.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/SSE.tsx index 64a9f5717114..4c0ae97036ad 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/SSE.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/SSE.tsx @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/react'; -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const fetchSSE = async ({ timeout, abort = false }: { timeout: boolean; abort?: boolean }) => { diff --git a/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/User.tsx index 62f0c2d17533..671455a92fff 100644 --- a/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/User.tsx +++ b/dev-packages/e2e-tests/test-applications/react-router-7-spa/src/pages/User.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const User = () => { diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/pages/Index.tsx b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/pages/Index.tsx index 7789a2773224..f339eb867d6c 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/pages/Index.tsx +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/pages/Index.tsx @@ -1,5 +1,4 @@ import * as Sentry from '@sentry/react'; -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Link } from 'react-router-dom'; diff --git a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/pages/User.tsx b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/pages/User.tsx index 62f0c2d17533..671455a92fff 100644 --- a/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/pages/User.tsx +++ b/dev-packages/e2e-tests/test-applications/react-send-to-sentry/src/pages/User.tsx @@ -1,4 +1,3 @@ -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; const User = () => { diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/.env b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.env new file mode 100644 index 000000000000..9b8dc350a98d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.env @@ -0,0 +1,2 @@ +SESSION_SECRET = "foo" +PUBLIC_STORE_DOMAIN="mock.shop" diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/.eslintignore b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.eslintignore new file mode 100644 index 000000000000..a362bcaa13b5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.eslintignore @@ -0,0 +1,5 @@ +build +node_modules +bin +*.d.ts +dist diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/.eslintrc.cjs b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.eslintrc.cjs new file mode 100644 index 000000000000..85eb86d14b9e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.eslintrc.cjs @@ -0,0 +1,79 @@ +/** + * This is intended to be a basic starting point for linting in your app. + * It relies on recommended configs out of the box for simplicity, but you can + * and should modify this configuration to best suit your team's needs. + */ + +/** @type {import('eslint').Linter.Config} */ +module.exports = { + root: true, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + env: { + browser: true, + commonjs: true, + es6: true, + }, + + // Base config + extends: ['eslint:recommended'], + + overrides: [ + // React + { + files: ['**/*.{js,jsx,ts,tsx}'], + plugins: ['react', 'jsx-a11y'], + extends: [ + 'plugin:react/recommended', + 'plugin:react/jsx-runtime', + 'plugin:react-hooks/recommended', + 'plugin:jsx-a11y/recommended', + ], + settings: { + react: { + version: 'detect', + }, + formComponents: ['Form'], + linkComponents: [ + { name: 'Link', linkAttribute: 'to' }, + { name: 'NavLink', linkAttribute: 'to' }, + ], + 'import/resolver': { + typescript: {}, + }, + }, + }, + + // Typescript + { + files: ['**/*.{ts,tsx}'], + plugins: ['@typescript-eslint', 'import'], + parser: '@typescript-eslint/parser', + settings: { + 'import/internal-regex': '^~/', + 'import/resolver': { + node: { + extensions: ['.ts', '.tsx'], + }, + typescript: { + alwaysTryTypes: true, + }, + }, + }, + extends: ['plugin:@typescript-eslint/recommended', 'plugin:import/recommended', 'plugin:import/typescript'], + }, + + // Node + { + files: ['.eslintrc.cjs', 'server.ts'], + env: { + node: true, + }, + }, + ], +}; diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/.gitignore b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.gitignore new file mode 100644 index 000000000000..4ee373874805 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.gitignore @@ -0,0 +1,9 @@ +node_modules +/.cache +/build +/dist +/public/build +/.mf +!.env +.shopify +storefrontapi.generated.d.ts diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/.npmrc b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/entry.client.tsx b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/entry.client.tsx new file mode 100644 index 000000000000..82d9ae571fe1 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/entry.client.tsx @@ -0,0 +1,37 @@ +import { RemixBrowser, useLocation, useMatches } from '@remix-run/react'; +import * as Sentry from '@sentry/remix/cloudflare'; +import { StrictMode, startTransition } from 'react'; +import { useEffect } from 'react'; +import { hydrateRoot } from 'react-dom/client'; + +Sentry.init({ + environment: 'qa', // dynamic sampling bias to keep transactions + // Could not find a working way to set the DSN in the browser side from the environment variables + dsn: 'https://public@dsn.ingest.sentry.io/1337', + debug: true, + integrations: [ + Sentry.browserTracingIntegration({ + useEffect, + useLocation, + useMatches, + }), + Sentry.replayIntegration({ + maskAllText: true, + blockAllMedia: true, + }), + ], + + tracesSampleRate: 1.0, + replaysSessionSampleRate: 0.1, + replaysOnErrorSampleRate: 1.0, + tunnel: 'http://localhost:3031/', // proxy server +}); + +startTransition(() => { + hydrateRoot( + document, + + + , + ); +}); diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/entry.server.tsx b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/entry.server.tsx new file mode 100644 index 000000000000..afae990db239 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/entry.server.tsx @@ -0,0 +1,50 @@ +import { RemixServer } from '@remix-run/react'; +import { createContentSecurityPolicy } from '@shopify/hydrogen'; +import type { EntryContext } from '@shopify/remix-oxygen'; +import isbot from 'isbot'; +import { renderToReadableStream } from 'react-dom/server'; + +export default async function handleRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + remixContext: EntryContext, +) { + const { nonce, header, NonceProvider } = createContentSecurityPolicy({ + connectSrc: [ + // Need to allow the proxy server to fetch the data + 'http://localhost:3031/', + ], + }); + + const body = await renderToReadableStream( + + + , + { + nonce, + signal: request.signal, + onError(error) { + // eslint-disable-next-line no-console + console.error(error); + responseStatusCode = 500; + }, + }, + ); + + if (isbot(request.headers.get('user-agent'))) { + await body.allReady; + } + + responseHeaders.set('Content-Type', 'text/html'); + responseHeaders.set('Content-Security-Policy', header); + + // Add the document policy header to enable JS profiling + // This is required for Sentry's profiling integration + responseHeaders.set('Document-Policy', 'js-profiling'); + + return new Response(body, { + headers: responseHeaders, + status: responseStatusCode, + }); +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/functions/_middleware.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/functions/_middleware.ts new file mode 100644 index 000000000000..de18f7ef68bb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/functions/_middleware.ts @@ -0,0 +1,12 @@ +import { createPagesFunctionHandler } from '@remix-run/cloudflare-pages'; +import { sentryPagesPlugin } from '@sentry/cloudflare'; + +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore - the server build file is generated by `remix vite:build` +// eslint-disable-next-line import/no-unresolved +import * as build from '../build/server'; + +export const onRequest = [ + context => sentryPagesPlugin({ dsn: context.env.E2E_TEST_DSN, tracesSampleRate: 1.0 })(context), + createPagesFunctionHandler({ build }), +]; diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/fragments.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/fragments.ts new file mode 100644 index 000000000000..ccf430475620 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/fragments.ts @@ -0,0 +1,174 @@ +// NOTE: https://shopify.dev/docs/api/storefront/latest/queries/cart +export const CART_QUERY_FRAGMENT = `#graphql + fragment Money on MoneyV2 { + currencyCode + amount + } + fragment CartLine on CartLine { + id + quantity + attributes { + key + value + } + cost { + totalAmount { + ...Money + } + amountPerQuantity { + ...Money + } + compareAtAmountPerQuantity { + ...Money + } + } + merchandise { + ... on ProductVariant { + id + availableForSale + compareAtPrice { + ...Money + } + price { + ...Money + } + requiresShipping + title + image { + id + url + altText + width + height + + } + product { + handle + title + id + vendor + } + selectedOptions { + name + value + } + } + } + } + fragment CartApiQuery on Cart { + updatedAt + id + checkoutUrl + totalQuantity + buyerIdentity { + countryCode + customer { + id + email + firstName + lastName + displayName + } + email + phone + } + lines(first: $numCartLines) { + nodes { + ...CartLine + } + } + cost { + subtotalAmount { + ...Money + } + totalAmount { + ...Money + } + totalDutyAmount { + ...Money + } + totalTaxAmount { + ...Money + } + } + note + attributes { + key + value + } + discountCodes { + code + applicable + } + } +` as const; + +const MENU_FRAGMENT = `#graphql + fragment MenuItem on MenuItem { + id + resourceId + tags + title + type + url + } + fragment ChildMenuItem on MenuItem { + ...MenuItem + } + fragment ParentMenuItem on MenuItem { + ...MenuItem + items { + ...ChildMenuItem + } + } + fragment Menu on Menu { + id + items { + ...ParentMenuItem + } + } +` as const; + +export const HEADER_QUERY = `#graphql + fragment Shop on Shop { + id + name + description + primaryDomain { + url + } + brand { + logo { + image { + url + } + } + } + } + query Header( + $country: CountryCode + $headerMenuHandle: String! + $language: LanguageCode + ) @inContext(language: $language, country: $country) { + shop { + ...Shop + } + menu(handle: $headerMenuHandle) { + ...Menu + } + } + ${MENU_FRAGMENT} +` as const; + +export const FOOTER_QUERY = `#graphql + query Footer( + $country: CountryCode + $footerMenuHandle: String! + $language: LanguageCode + ) @inContext(language: $language, country: $country) { + menu(handle: $footerMenuHandle) { + ...Menu + } + } + ${MENU_FRAGMENT} +` as const; diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/search.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/search.ts new file mode 100644 index 000000000000..d3295f1fc66a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/search.ts @@ -0,0 +1,25 @@ +import type { + PredictiveArticleFragment, + PredictiveCollectionFragment, + PredictivePageFragment, + PredictiveProductFragment, + PredictiveQueryFragment, + SearchProductFragment, +} from 'storefrontapi.generated'; + +export function applyTrackingParams( + resource: + | PredictiveQueryFragment + | SearchProductFragment + | PredictiveProductFragment + | PredictiveCollectionFragment + | PredictiveArticleFragment + | PredictivePageFragment, + params?: string, +) { + if (params) { + return resource?.trackingParameters ? `?${params}&${resource.trackingParameters}` : `?${params}`; + } else { + return resource?.trackingParameters ? `?${resource.trackingParameters}` : ''; + } +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/session.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/session.ts new file mode 100644 index 000000000000..80d6e7b86b52 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/session.ts @@ -0,0 +1,61 @@ +import type { HydrogenSession } from '@shopify/hydrogen'; +import { type Session, type SessionStorage, createCookieSessionStorage } from '@shopify/remix-oxygen'; + +/** + * This is a custom session implementation for your Hydrogen shop. + * Feel free to customize it to your needs, add helper methods, or + * swap out the cookie-based implementation with something else! + */ +export class AppSession implements HydrogenSession { + #sessionStorage; + #session; + + constructor(sessionStorage: SessionStorage, session: Session) { + this.#sessionStorage = sessionStorage; + this.#session = session; + } + + static async init(request: Request, secrets: string[]) { + const storage = createCookieSessionStorage({ + cookie: { + name: 'session', + httpOnly: true, + path: '/', + sameSite: 'lax', + secrets, + }, + }); + + const session = await storage.getSession(request.headers.get('Cookie')).catch(() => storage.getSession()); + + return new this(storage, session); + } + + get has() { + return this.#session.has; + } + + get get() { + return this.#session.get; + } + + get flash() { + return this.#session.flash; + } + + get unset() { + return this.#session.unset; + } + + get set() { + return this.#session.set; + } + + destroy() { + return this.#sessionStorage.destroySession(this.#session); + } + + commit() { + return this.#sessionStorage.commitSession(this.#session); + } +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/variants.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/variants.ts new file mode 100644 index 000000000000..0b8fbabdd528 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/lib/variants.ts @@ -0,0 +1,41 @@ +import { useLocation } from '@remix-run/react'; +import type { SelectedOption } from '@shopify/hydrogen/storefront-api-types'; +import { useMemo } from 'react'; + +export function useVariantUrl(handle: string, selectedOptions: SelectedOption[]) { + const { pathname } = useLocation(); + + return useMemo(() => { + return getVariantUrl({ + handle, + pathname, + searchParams: new URLSearchParams(), + selectedOptions, + }); + }, [handle, selectedOptions, pathname]); +} + +export function getVariantUrl({ + handle, + pathname, + searchParams, + selectedOptions, +}: { + handle: string; + pathname: string; + searchParams: URLSearchParams; + selectedOptions: SelectedOption[]; +}) { + const match = /(\/[a-zA-Z]{2}-[a-zA-Z]{2}\/)/g.exec(pathname); + const isLocalePathname = match && match.length > 0; + + const path = isLocalePathname ? `${match![0]}products/${handle}` : `/products/${handle}`; + + selectedOptions.forEach(option => { + searchParams.set(option.name, option.value); + }); + + const searchString = searchParams.toString(); + + return path + (searchString ? '?' + searchParams.toString() : ''); +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/root.tsx b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/root.tsx new file mode 100644 index 000000000000..ef098d204e10 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/root.tsx @@ -0,0 +1,279 @@ +import { + Links, + LiveReload, + Meta, + type MetaFunction, + Outlet, + Scripts, + ScrollRestoration, + type ShouldRevalidateFunction, + isRouteErrorResponse, + useLocation, + useMatches, + useRouteError, +} from '@remix-run/react'; + +import * as Sentry from '@sentry/remix/cloudflare'; +import { useNonce } from '@shopify/hydrogen'; +import type { CustomerAccessToken } from '@shopify/hydrogen/storefront-api-types'; +import { type LoaderArgs, defer } from '@shopify/remix-oxygen'; +import { useEffect } from 'react'; +import favicon from '../public/favicon.svg'; +import type { HydrogenSession } from '../server'; + +// This is important to avoid re-fetching root queries on sub-navigations +export const shouldRevalidate: ShouldRevalidateFunction = ({ formMethod, currentUrl, nextUrl }) => { + // revalidate when a mutation is performed e.g add to cart, login... + if (formMethod && formMethod !== 'GET') { + return true; + } + + // revalidate when manually revalidating via useRevalidator + if (currentUrl.toString() === nextUrl.toString()) { + return true; + } + + return false; +}; + +export function links() { + return [ + { + rel: 'preconnect', + href: 'https://cdn.shopify.com', + }, + { + rel: 'preconnect', + href: 'https://shop.app', + }, + { rel: 'icon', type: 'image/svg+xml', href: favicon }, + ]; +} + +export async function loader({ context }: LoaderArgs) { + const { storefront, session, cart } = context; + const customerAccessToken = await session.get('customerAccessToken'); + const publicStoreDomain = context.env.PUBLIC_STORE_DOMAIN; + + // validate the customer access token is valid + const { isLoggedIn, headers } = await validateCustomerAccessToken(session, customerAccessToken); + + // defer the cart query by not awaiting it + const cartPromise = cart.get(); + + // defer the footer query (below the fold) + const footerPromise = storefront.query(FOOTER_QUERY, { + cache: storefront.CacheLong(), + variables: { + footerMenuHandle: 'footer', // Adjust to your footer menu handle + }, + }); + + // await the header query (above the fold) + const headerPromise = storefront.query(HEADER_QUERY, { + cache: storefront.CacheLong(), + variables: { + headerMenuHandle: 'main-menu', // Adjust to your header menu handle + }, + }); + + return defer( + { + cart: cartPromise, + footer: footerPromise, + header: await headerPromise, + isLoggedIn, + publicStoreDomain, + }, + { headers }, + ); +} + +export const meta = ({ data }: Sentry.SentryMetaArgs>) => { + return [ + { + name: 'sentry-trace', + content: data.sentryTrace, + }, + { + name: 'baggage', + content: data.sentryBaggage, + }, + ]; +}; + +function App() { + const nonce = useNonce(); + + return ( + + + + + + + + + + + + + + + ); +} + +// Need to supply the hooks to Sentry.withSentry for hydrogen apps +export default Sentry.withSentry(App, useEffect, useLocation, useMatches); + +export function ErrorBoundary() { + const error = useRouteError(); + const [root] = useMatches(); + const nonce = useNonce(); + let errorMessage = 'Unknown error'; + let errorStatus = 500; + + // Send the error to Sentry + const eventId = Sentry.captureRemixErrorBoundaryError(error); + + if (isRouteErrorResponse(error)) { + errorMessage = error?.data?.message ?? error.data; + errorStatus = error.status; + } else if (error instanceof Error) { + errorMessage = error.message; + } + + return ( + + + + + + + + +
+

Oops

+

{errorStatus}

+ {errorMessage && ( +
+
{errorMessage}
+
+ )} + {eventId && ( +

+ Sentry Event ID: {eventId} +

+ )} +
+ + + + + + ); +} + +/** + * Validates the customer access token and returns a boolean and headers + * @see https://shopify.dev/docs/api/storefront/latest/objects/CustomerAccessToken + * + * @example + * ```ts + * // + * const {isLoggedIn, headers} = await validateCustomerAccessToken( + * customerAccessToken, + * session, + * ); + * ``` + * */ +async function validateCustomerAccessToken(session: HydrogenSession, customerAccessToken?: CustomerAccessToken) { + let isLoggedIn = false; + const headers = new Headers(); + if (!customerAccessToken?.accessToken || !customerAccessToken?.expiresAt) { + return { isLoggedIn, headers }; + } + + const expiresAt = new Date(customerAccessToken.expiresAt).getTime(); + const dateNow = Date.now(); + const customerAccessTokenExpired = expiresAt < dateNow; + + if (customerAccessTokenExpired) { + session.unset('customerAccessToken'); + headers.append('Set-Cookie', await session.commit()); + } else { + isLoggedIn = true; + } + + return { isLoggedIn, headers }; +} + +const MENU_FRAGMENT = `#graphql + fragment MenuItem on MenuItem { + id + resourceId + tags + title + type + url + } + fragment ChildMenuItem on MenuItem { + ...MenuItem + } + fragment ParentMenuItem on MenuItem { + ...MenuItem + items { + ...ChildMenuItem + } + } + fragment Menu on Menu { + id + items { + ...ParentMenuItem + } + } +` as const; + +const HEADER_QUERY = `#graphql + fragment Shop on Shop { + id + name + description + primaryDomain { + url + } + brand { + logo { + image { + url + } + } + } + } + query Header( + $country: CountryCode + $headerMenuHandle: String! + $language: LanguageCode + ) @inContext(language: $language, country: $country) { + shop { + ...Shop + } + menu(handle: $headerMenuHandle) { + ...Menu + } + } + ${MENU_FRAGMENT} +` as const; + +const FOOTER_QUERY = `#graphql + query Footer( + $country: CountryCode + $footerMenuHandle: String! + $language: LanguageCode + ) @inContext(language: $language, country: $country) { + menu(handle: $footerMenuHandle) { + ...Menu + } + } + ${MENU_FRAGMENT} +` as const; diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/_index.tsx b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/_index.tsx new file mode 100644 index 000000000000..48e9494f1c2c --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/_index.tsx @@ -0,0 +1,29 @@ +import { Link, useSearchParams } from '@remix-run/react'; +import * as Sentry from '@sentry/remix/cloudflare'; + +export default function Index() { + const [searchParams] = useSearchParams(); + + if (searchParams.get('tag')) { + Sentry.setTags({ + sentry_test: searchParams.get('tag'), + }); + } + + return ( +
+ { + const eventId = Sentry.captureException(new Error('I am an error!')); + window.capturedExceptionId = eventId; + }} + /> + + navigate + +
+ ); +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/action-formdata.tsx b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/action-formdata.tsx new file mode 100644 index 000000000000..9c029b377b08 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/action-formdata.tsx @@ -0,0 +1,17 @@ +import { Form } from '@remix-run/react'; +import { json } from '@shopify/remix-oxygen'; + +export async function action() { + return json({ message: 'success' }); +} + +export default function ActionFormData() { + return ( +
+ + + + +
+ ); +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/client-error.tsx b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/client-error.tsx new file mode 100644 index 000000000000..81859fb3d1ba --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/client-error.tsx @@ -0,0 +1,24 @@ +import { useSearchParams } from '@remix-run/react'; +import * as Sentry from '@sentry/remix/cloudflare'; + +import { useState } from 'react'; + +export default function ErrorBoundaryCapture() { + const [searchParams] = useSearchParams(); + + if (searchParams.get('tag')) { + Sentry.setTags({ + sentry_test: searchParams.get('tag'), + }); + } + + const [count, setCount] = useState(0); + + if (count > 0) { + throw new Error('Sentry React Component Error'); + } else { + setTimeout(() => setCount(count + 1), 0); + } + + return
{count}
; +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/loader-error.tsx b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/loader-error.tsx new file mode 100644 index 000000000000..16e1ac5e83bb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/loader-error.tsx @@ -0,0 +1,16 @@ +import { useLoaderData } from '@remix-run/react'; +import type { LoaderFunction } from '@shopify/remix-oxygen'; + +export default function LoaderError() { + useLoaderData(); + + return ( +
+

Loader Error

+
+ ); +} + +export const loader: LoaderFunction = () => { + throw new Error('Loader Error'); +}; diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/navigate.tsx b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/navigate.tsx new file mode 100644 index 000000000000..7fe190a6eb77 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/navigate.tsx @@ -0,0 +1,20 @@ +import { useLoaderData } from '@remix-run/react'; +import type { LoaderFunction } from '@shopify/remix-oxygen'; + +export const loader: LoaderFunction = async ({ params: { id } }) => { + if (id === '-1') { + throw new Error('Unexpected Server Error'); + } + + return null; +}; + +export default function LoaderError() { + const data = useLoaderData(); + + return ( +
+

{data && data.test ? data.test : 'Not Found'}

+
+ ); +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/user.$id.tsx b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/user.$id.tsx new file mode 100644 index 000000000000..13b2e0a34d1e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/routes/user.$id.tsx @@ -0,0 +1,3 @@ +export default function User() { + return
I am a blank page
; +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/utils.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/utils.ts new file mode 100644 index 000000000000..0b8fbabdd528 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/app/utils.ts @@ -0,0 +1,41 @@ +import { useLocation } from '@remix-run/react'; +import type { SelectedOption } from '@shopify/hydrogen/storefront-api-types'; +import { useMemo } from 'react'; + +export function useVariantUrl(handle: string, selectedOptions: SelectedOption[]) { + const { pathname } = useLocation(); + + return useMemo(() => { + return getVariantUrl({ + handle, + pathname, + searchParams: new URLSearchParams(), + selectedOptions, + }); + }, [handle, selectedOptions, pathname]); +} + +export function getVariantUrl({ + handle, + pathname, + searchParams, + selectedOptions, +}: { + handle: string; + pathname: string; + searchParams: URLSearchParams; + selectedOptions: SelectedOption[]; +}) { + const match = /(\/[a-zA-Z]{2}-[a-zA-Z]{2}\/)/g.exec(pathname); + const isLocalePathname = match && match.length > 0; + + const path = isLocalePathname ? `${match![0]}products/${handle}` : `/products/${handle}`; + + selectedOptions.forEach(option => { + searchParams.set(option.name, option.value); + }); + + const searchString = searchParams.toString(); + + return path + (searchString ? '?' + searchParams.toString() : ''); +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/env.d.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/env.d.ts new file mode 100644 index 000000000000..6ae256061aa8 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/env.d.ts @@ -0,0 +1,49 @@ +/// +/// +/// + +// Enhance TypeScript's built-in typings. +import '@total-typescript/ts-reset'; + +import type { CustomerAccount, HydrogenCart, HydrogenSessionData, Storefront } from '@shopify/hydrogen'; +import type { AppSession } from '~/lib/session'; + +declare global { + /** + * A global `process` object is only available during build to access NODE_ENV. + */ + const process: { env: { NODE_ENV: 'production' | 'development' } }; + + /** + * Declare expected Env parameter in fetch handler. + */ + interface Env { + SESSION_SECRET: string; + PUBLIC_STOREFRONT_API_TOKEN: string; + PRIVATE_STOREFRONT_API_TOKEN: string; + PUBLIC_STORE_DOMAIN: string; + PUBLIC_STOREFRONT_ID: string; + PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID: string; + PUBLIC_CUSTOMER_ACCOUNT_API_URL: string; + PUBLIC_CHECKOUT_DOMAIN: string; + } +} + +declare module '@shopify/remix-oxygen' { + /** + * Declare local additions to the Remix loader context. + */ + interface AppLoadContext { + env: Env; + cart: HydrogenCart; + storefront: Storefront; + customerAccount: CustomerAccount; + session: AppSession; + waitUntil: ExecutionContext['waitUntil']; + } + + /** + * Declare local additions to the Remix session data. + */ + interface SessionData extends HydrogenSessionData {} +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/globals.d.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/globals.d.ts new file mode 100644 index 000000000000..4130ac6a8a09 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/globals.d.ts @@ -0,0 +1,7 @@ +interface Window { + recordedTransactions?: string[]; + capturedExceptionId?: string; + ENV: { + SENTRY_DSN: string; + }; +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/package.json b/dev-packages/e2e-tests/test-applications/remix-hydrogen/package.json new file mode 100644 index 000000000000..742cd1526beb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/package.json @@ -0,0 +1,58 @@ +{ + "private": true, + "sideEffects": false, + "type": "module", + "scripts": { + "build": "shopify hydrogen build --codegen", + "dev": "shopify hydrogen dev --codegen", + "preview": "shopify hydrogen preview", + "lint": "eslint --no-error-on-unmatched-pattern --ext .js,.ts,.jsx,.tsx .", + "typecheck": "tsc --noEmit", + "codegen": "shopify hydrogen codegen", + "clean": "npx rimraf node_modules dist pnpm-lock.yaml", + "test:build": "pnpm install && npx playwright install && pnpm build", + "test:assert": "pnpm playwright test" + }, + "prettier": "@shopify/prettier-config", + "dependencies": { + "@remix-run/react": "^2.15.2", + "@remix-run/server-runtime": "^2.15.2", + "@sentry/cloudflare": "latest || *", + "@sentry/remix": "latest || *", + "@sentry/vite-plugin": "^3.1.2", + "@shopify/hydrogen": "^2025.1.0", + "@shopify/remix-oxygen": "^2.0.10", + "graphql": "^16.6.0", + "graphql-tag": "^2.12.6", + "isbot": "^3.8.0", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@graphql-codegen/cli": "5.0.2", + "@playwright/test": "^1.44.1", + "@remix-run/dev": "^2.15.2", + "@remix-run/eslint-config": "^2.15.2", + "@sentry-internal/test-utils": "link:../../../test-utils", + "@shopify/cli": "^3.74.1", + "@shopify/hydrogen-codegen": "^0.3.1", + "@shopify/mini-oxygen": "^3.1.1", + "@shopify/oxygen-workers-types": "^4.1.2", + "@shopify/prettier-config": "^1.1.2", + "@tailwindcss/vite": "4.0.0-alpha.17", + "@total-typescript/ts-reset": "^0.4.2", + "@types/eslint": "^8.4.10", + "@types/react": "^18.2.22", + "@types/react-dom": "^18.2.7", + "esbuild": "0.25.0", + "eslint": "^8.20.0", + "eslint-plugin-hydrogen": "0.12.2", + "prettier": "^2.8.4", + "typescript": "^5.2.2", + "vite": "^5.1.0", + "vite-tsconfig-paths": "^4.3.1" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/remix-hydrogen/playwright.config.mjs new file mode 100644 index 000000000000..700607cc6f95 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/playwright.config.mjs @@ -0,0 +1,8 @@ +import { getPlaywrightConfig } from '@sentry-internal/test-utils'; + +const config = getPlaywrightConfig({ + startCommand: `pnpm run preview`, + port: 3000, +}); + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/public/favicon.ico b/dev-packages/e2e-tests/test-applications/remix-hydrogen/public/favicon.ico new file mode 100644 index 000000000000..f6c649733d68 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/public/favicon.ico @@ -0,0 +1,28 @@ + + + + + diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/public/favicon.svg b/dev-packages/e2e-tests/test-applications/remix-hydrogen/public/favicon.svg new file mode 100644 index 000000000000..f6c649733d68 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/public/favicon.svg @@ -0,0 +1,28 @@ + + + + + diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/server.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/server.ts new file mode 100644 index 000000000000..6a3a889cf968 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/server.ts @@ -0,0 +1,123 @@ +import { wrapRequestHandler } from '@sentry/cloudflare/request'; +import { instrumentBuild } from '@sentry/remix/cloudflare'; +import { + cartGetIdDefault, + cartSetIdDefault, + createCartHandler, + createCustomerAccountClient, + createStorefrontClient, + storefrontRedirect, +} from '@shopify/hydrogen'; +import { type AppLoadContext, createRequestHandler, getStorefrontHeaders } from '@shopify/remix-oxygen'; +import { CART_QUERY_FRAGMENT } from '~/lib/fragments'; +import { AppSession } from '~/lib/session'; +// Virtual entry point for the app +import * as remixBuild from 'virtual:remix/server-build'; + +/** + * Export a fetch handler in module format. + */ +export default { + async fetch(request: Request, env: Env, executionContext: ExecutionContext): Promise { + return wrapRequestHandler( + { + options: { + environment: 'qa', // dynamic sampling bias to keep transactions + dsn: 'https://public@dsn.ingest.sentry.io/1337', + tracesSampleRate: 1.0, + tunnel: `http://localhost:3031/`, // proxy server + }, + // Need to cast to any because this is not on cloudflare + request: request as any, + context: executionContext, + }, + async () => { + try { + /** + * Open a cache instance in the worker and a custom session instance. + */ + if (!env?.SESSION_SECRET) { + throw new Error('SESSION_SECRET environment variable is not set'); + } + + const waitUntil = executionContext.waitUntil.bind(executionContext); + const [cache, session] = await Promise.all([ + caches.open('hydrogen'), + AppSession.init(request, [env.SESSION_SECRET]), + ]); + + /** + * Create Hydrogen's Storefront client. + */ + const { storefront } = createStorefrontClient({ + cache, + waitUntil, + i18n: { language: 'EN', country: 'US' }, + publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN, + privateStorefrontToken: env.PRIVATE_STOREFRONT_API_TOKEN, + storeDomain: env.PUBLIC_STORE_DOMAIN, + storefrontId: env.PUBLIC_STOREFRONT_ID, + storefrontHeaders: getStorefrontHeaders(request), + }); + + /** + * Create a client for Customer Account API. + */ + const customerAccount = createCustomerAccountClient({ + waitUntil, + request, + session, + customerAccountId: env.PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID, + customerAccountUrl: env.PUBLIC_CUSTOMER_ACCOUNT_API_URL, + }); + + /* + * Create a cart handler that will be used to + * create and update the cart in the session. + */ + const cart = createCartHandler({ + storefront, + customerAccount, + getCartId: cartGetIdDefault(request.headers), + setCartId: cartSetIdDefault(), + cartQueryFragment: CART_QUERY_FRAGMENT, + }); + + /** + * Create a Remix request handler and pass + * Hydrogen's Storefront client to the loader context. + */ + const handleRequest = createRequestHandler({ + build: instrumentBuild(remixBuild), + mode: process.env.NODE_ENV, + getLoadContext: (): AppLoadContext => ({ + session, + storefront, + customerAccount, + cart, + env, + waitUntil, + }), + }); + + const response = await handleRequest(request); + + if (response.status === 404) { + /** + * Check for redirects only when there's a 404 from the app. + * If the redirect doesn't exist, then `storefrontRedirect` + * will pass through the 404 response. + */ + return storefrontRedirect({ request, response, storefront }); + } + + return response; + } catch (error) { + // eslint-disable-next-line no-console + console.error(error); + return new Response('An unexpected error occurred', { status: 500 }); + } + }, + ); + }, +}; diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/start-event-proxy.mjs b/dev-packages/e2e-tests/test-applications/remix-hydrogen/start-event-proxy.mjs new file mode 100644 index 000000000000..fa42041e54a3 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/start-event-proxy.mjs @@ -0,0 +1,6 @@ +import { startEventProxyServer } from '@sentry-internal/test-utils'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'remix-hydrogen', +}); diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/tests/client-errors.test.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/tests/client-errors.test.ts new file mode 100644 index 000000000000..747ce8677afb --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/tests/client-errors.test.ts @@ -0,0 +1,29 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '@sentry-internal/test-utils'; + +test('Sends a client-side exception to Sentry', async ({ page }) => { + const errorPromise = waitForError('remix-hydrogen', errorEvent => { + return errorEvent.exception?.values?.[0].value === 'I am an error!'; + }); + + await page.goto('/'); + + const exceptionButton = page.locator('id=exception-button'); + await exceptionButton.click(); + + const errorEvent = await errorPromise; + + expect(errorEvent).toBeDefined(); +}); + +test('Sends a client-side ErrorBoundary exception to Sentry', async ({ page }) => { + const errorPromise = waitForError('remix-hydrogen', errorEvent => { + return errorEvent.exception?.values?.[0].value === 'Sentry React Component Error'; + }); + + await page.goto('/client-error'); + + const errorEvent = await errorPromise; + + expect(errorEvent).toBeDefined(); +}); diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/tests/client-transactions.test.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/tests/client-transactions.test.ts new file mode 100644 index 000000000000..918fcba1fb3d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/tests/client-transactions.test.ts @@ -0,0 +1,60 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test('Sends a pageload transaction to Sentry', async ({ page }) => { + const transactionPromise = waitForTransaction('remix-hydrogen', transactionEvent => { + return transactionEvent.contexts?.trace?.op === 'pageload' && transactionEvent.transaction === 'routes/_index'; + }); + + await page.goto('/'); + + const transactionEvent = await transactionPromise; + + expect(transactionEvent).toBeDefined(); +}); + +test('Sends a navigation transaction to Sentry', async ({ page }) => { + const transactionPromise = waitForTransaction('remix-hydrogen', transactionEvent => { + return transactionEvent.contexts?.trace?.op === 'navigation' && transactionEvent.transaction === 'routes/user.$id'; + }); + + await page.goto('/'); + + const linkElement = page.locator('id=navigation'); + await linkElement.click(); + + const transactionEvent = await transactionPromise; + + expect(transactionEvent).toBeDefined(); + expect(transactionEvent).toMatchObject({ + transaction: 'routes/user.$id', + }); +}); + +test('Renders `sentry-trace` and `baggage` meta tags for the root route', async ({ page }) => { + await page.goto('/'); + + const sentryTraceMetaTag = await page.waitForSelector('meta[name="sentry-trace"]', { + state: 'attached', + }); + const baggageMetaTag = await page.waitForSelector('meta[name="baggage"]', { + state: 'attached', + }); + + expect(sentryTraceMetaTag).toBeTruthy(); + expect(baggageMetaTag).toBeTruthy(); +}); + +test('Renders `sentry-trace` and `baggage` meta tags for a sub-route', async ({ page }) => { + await page.goto('/user/123'); + + const sentryTraceMetaTag = await page.waitForSelector('meta[name="sentry-trace"]', { + state: 'attached', + }); + const baggageMetaTag = await page.waitForSelector('meta[name="baggage"]', { + state: 'attached', + }); + + expect(sentryTraceMetaTag).toBeTruthy(); + expect(baggageMetaTag).toBeTruthy(); +}); diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/tests/server-transactions.test.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/tests/server-transactions.test.ts new file mode 100644 index 000000000000..2a13d40e2c54 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/tests/server-transactions.test.ts @@ -0,0 +1,57 @@ +import { expect, test } from '@playwright/test'; +import { uuid4 } from '@sentry/core'; + +import { waitForTransaction } from '@sentry-internal/test-utils'; + +test.describe.configure({ mode: 'serial' }); + +test('Sends parameterized transaction name to Sentry', async ({ page }) => { + const transactionPromise = waitForTransaction('remix-hydrogen', transactionEvent => { + return transactionEvent.contexts?.trace?.op === 'http.server'; + }); + + await page.goto('/user/123'); + + const transaction = await transactionPromise; + + expect(transaction).toBeDefined(); + expect(transaction.transaction).toBe('GET /user/123'); +}); + +test('Sends two linked transactions (server & client) to Sentry', async ({ page }) => { + // We use this to identify the transactions + const testTag = uuid4(); + + const httpServerTransactionPromise = waitForTransaction('remix-hydrogen', transactionEvent => { + return transactionEvent.contexts?.trace?.op === 'http.server' && transactionEvent.tags?.['sentry_test'] === testTag; + }); + + const pageLoadTransactionPromise = waitForTransaction('remix-hydrogen', transactionEvent => { + return transactionEvent.contexts?.trace?.op === 'pageload' && transactionEvent.tags?.['sentry_test'] === testTag; + }); + + page.goto(`/?tag=${testTag}`); + + const pageloadTransaction = await pageLoadTransactionPromise; + const httpServerTransaction = await httpServerTransactionPromise; + + expect(pageloadTransaction).toBeDefined(); + expect(httpServerTransaction).toBeDefined(); + + const httpServerTraceId = httpServerTransaction.contexts?.trace?.trace_id; + const httpServerSpanId = httpServerTransaction.contexts?.trace?.span_id; + const loaderSpanId = httpServerTransaction?.spans?.find(span => span.op === 'function.remix.loader')?.span_id; + + const pageLoadTraceId = pageloadTransaction.contexts?.trace?.trace_id; + const pageLoadSpanId = pageloadTransaction.contexts?.trace?.span_id; + + expect(httpServerTransaction.transaction).toBe('GET /'); + expect(pageloadTransaction.transaction).toBe('routes/_index'); + + expect(httpServerTraceId).toBeDefined(); + expect(httpServerSpanId).toBeDefined(); + expect(loaderSpanId).toBeDefined(); + + expect(pageLoadTraceId).toEqual(httpServerTraceId); + expect(pageLoadSpanId).not.toEqual(httpServerSpanId); +}); diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/tsconfig.json b/dev-packages/e2e-tests/test-applications/remix-hydrogen/tsconfig.json new file mode 100644 index 000000000000..dcd7c7237a90 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/tsconfig.json @@ -0,0 +1,23 @@ +{ + "include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"], + "compilerOptions": { + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "isolatedModules": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "module": "ES2022", + "target": "ES2022", + "strict": true, + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "skipLibCheck": true, + "baseUrl": ".", + "types": ["@shopify/oxygen-workers-types"], + "paths": { + "~/*": ["app/*"] + }, + "noEmit": true + } +} diff --git a/dev-packages/e2e-tests/test-applications/remix-hydrogen/vite.config.ts b/dev-packages/e2e-tests/test-applications/remix-hydrogen/vite.config.ts new file mode 100644 index 000000000000..03a2f7539924 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/remix-hydrogen/vite.config.ts @@ -0,0 +1,46 @@ +import { vitePlugin as remix } from '@remix-run/dev'; +import { hydrogen } from '@shopify/hydrogen/vite'; +import { oxygen } from '@shopify/mini-oxygen/vite'; +import { defineConfig } from 'vite'; +import tsconfigPaths from 'vite-tsconfig-paths'; + +export default defineConfig({ + plugins: [ + hydrogen(), + oxygen(), + remix({ + presets: [hydrogen.preset()], + future: { + v3_fetcherPersist: true, + v3_relativeSplatPath: true, + v3_throwAbortReason: true, + }, + }), + tsconfigPaths({ + // The dev server config errors are not relevant to this test app + // https://github.com/aleclarson/vite-tsconfig-paths?tab=readme-ov-file#options + ignoreConfigErrors: true, + }), + ], + build: { + // Allow a strict Content-Security-Policy + // without inlining assets as base64: + assetsInlineLimit: 0, + minify: false, + }, + ssr: { + optimizeDeps: { + /** + * Include dependencies here if they throw CJS<>ESM errors. + * For example, for the following error: + * + * > ReferenceError: module is not defined + * > at /Users/.../node_modules/example-dep/index.js:1:1 + * + * Include 'example-dep' in the array below. + * @see https://vitejs.dev/config/dep-optimization-options + */ + include: ['hoist-non-react-statics', '@sentry/remix'], + }, + }, +}); diff --git a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/package.json b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/package.json index 495b9cb8c94d..14931bfd338c 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/package.json +++ b/dev-packages/e2e-tests/test-applications/solidstart-dynamic-import/package.json @@ -24,7 +24,7 @@ "@testing-library/user-event": "^14.5.2", "@vitest/ui": "^1.5.0", "jsdom": "^24.0.0", - "solid-js": "1.8.17", + "solid-js": "1.9.5", "typescript": "^5.4.5", "vinxi": "^0.4.0", "vite": "^5.4.10", diff --git a/dev-packages/e2e-tests/test-applications/solidstart-spa/package.json b/dev-packages/e2e-tests/test-applications/solidstart-spa/package.json index 57a83c35b0e8..69542682efa3 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-spa/package.json +++ b/dev-packages/e2e-tests/test-applications/solidstart-spa/package.json @@ -24,7 +24,7 @@ "@testing-library/user-event": "^14.5.2", "@vitest/ui": "^1.5.0", "jsdom": "^24.0.0", - "solid-js": "1.8.17", + "solid-js": "1.9.5", "typescript": "^5.4.5", "vinxi": "^0.4.0", "vite": "^5.4.11", diff --git a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/package.json b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/package.json index 3d96e0481158..fbcb5dda1bba 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/package.json +++ b/dev-packages/e2e-tests/test-applications/solidstart-top-level-import/package.json @@ -24,7 +24,7 @@ "@testing-library/user-event": "^14.5.2", "@vitest/ui": "^1.5.0", "jsdom": "^24.0.0", - "solid-js": "1.8.17", + "solid-js": "1.9.5", "typescript": "^5.4.5", "vinxi": "^0.4.0", "vite": "^5.4.11", diff --git a/dev-packages/e2e-tests/test-applications/solidstart/package.json b/dev-packages/e2e-tests/test-applications/solidstart/package.json index 2bb3afaa29b1..8f31e4447f1b 100644 --- a/dev-packages/e2e-tests/test-applications/solidstart/package.json +++ b/dev-packages/e2e-tests/test-applications/solidstart/package.json @@ -24,7 +24,7 @@ "@testing-library/user-event": "^14.5.2", "@vitest/ui": "^1.5.0", "jsdom": "^24.0.0", - "solid-js": "1.8.17", + "solid-js": "1.9.5", "typescript": "^5.4.5", "vinxi": "^0.4.0", "vite": "^5.4.11", diff --git a/dev-packages/e2e-tests/verdaccio-config/config.yaml b/dev-packages/e2e-tests/verdaccio-config/config.yaml index cbb73201eebf..af7d62521c59 100644 --- a/dev-packages/e2e-tests/verdaccio-config/config.yaml +++ b/dev-packages/e2e-tests/verdaccio-config/config.yaml @@ -170,6 +170,12 @@ packages: unpublish: $all # proxy: npmjs # Don't proxy for E2E tests! + '@sentry/tanstackstart': + access: $all + publish: $all + unpublish: $all + # proxy: npmjs # Don't proxy for E2E tests! + '@sentry/types': access: $all publish: $all diff --git a/dev-packages/node-integration-tests/jest.config.js b/dev-packages/node-integration-tests/jest.config.js deleted file mode 100644 index 9be635aba9ad..000000000000 --- a/dev-packages/node-integration-tests/jest.config.js +++ /dev/null @@ -1,9 +0,0 @@ -const baseConfig = require('../../jest/jest.config.js'); - -module.exports = { - globalSetup: '/utils/setup-tests.ts', - ...baseConfig, - testMatch: ['**/test.ts'], - setupFilesAfterEnv: ['./jest.setup.js'], - coverageReporters: ['json', 'lcov', 'clover'], -}; diff --git a/dev-packages/node-integration-tests/jest.setup.js b/dev-packages/node-integration-tests/jest.setup.js deleted file mode 100644 index 88492fc5d945..000000000000 --- a/dev-packages/node-integration-tests/jest.setup.js +++ /dev/null @@ -1,8 +0,0 @@ -const { cleanupChildProcesses } = require('./utils/runner'); - -// Default timeout: 15s -jest.setTimeout(15000); - -afterEach(() => { - cleanupChildProcesses(); -}); diff --git a/dev-packages/node-integration-tests/package.json b/dev-packages/node-integration-tests/package.json index 52d99207f089..e57cb085bc91 100644 --- a/dev-packages/node-integration-tests/package.json +++ b/dev-packages/node-integration-tests/package.json @@ -21,8 +21,7 @@ "fix": "eslint . --format stylish --fix", "type-check": "tsc", "pretest": "yarn express-v5-install", - "test": "jest --config ./jest.config.js", - "test:no-prisma": "jest --config ./jest.config.js", + "test": "vitest run", "test:watch": "yarn test --watch" }, "dependencies": { diff --git a/dev-packages/node-integration-tests/suites/anr/test.ts b/dev-packages/node-integration-tests/suites/anr/test.ts index 9a91d4f205c6..ad3647ec3974 100644 --- a/dev-packages/node-integration-tests/suites/anr/test.ts +++ b/dev-packages/node-integration-tests/suites/anr/test.ts @@ -1,4 +1,5 @@ import type { Event } from '@sentry/core'; +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../utils/runner'; const ANR_EVENT = { @@ -111,18 +112,23 @@ describe('should report ANR when event loop blocked', () => { cleanupChildProcesses(); }); - test('CJS', done => { - createRunner(__dirname, 'basic.js').withMockSentryServer().expect({ event: ANR_EVENT_WITH_DEBUG_META }).start(done); + test('CJS', async () => { + await createRunner(__dirname, 'basic.js') + .withMockSentryServer() + .expect({ event: ANR_EVENT_WITH_DEBUG_META }) + .start() + .completed(); }); - test('ESM', done => { - createRunner(__dirname, 'basic.mjs') + test('ESM', async () => { + await createRunner(__dirname, 'basic.mjs') .withMockSentryServer() .expect({ event: ANR_EVENT_WITH_DEBUG_META }) - .start(done); + .start() + .completed(); }); - test('Custom appRootPath', done => { + test('Custom appRootPath', async () => { const ANR_EVENT_WITH_SPECIFIC_DEBUG_META: Event = { ...ANR_EVENT_WITH_SCOPE, debug_meta: { @@ -136,52 +142,57 @@ describe('should report ANR when event loop blocked', () => { }, }; - createRunner(__dirname, 'app-path.mjs') + await createRunner(__dirname, 'app-path.mjs') .withMockSentryServer() .expect({ event: ANR_EVENT_WITH_SPECIFIC_DEBUG_META }) - .start(done); + .start() + .completed(); }); - test('multiple events via maxAnrEvents', done => { - createRunner(__dirname, 'basic-multiple.mjs') + test('multiple events via maxAnrEvents', async () => { + await createRunner(__dirname, 'basic-multiple.mjs') .withMockSentryServer() .expect({ event: ANR_EVENT_WITH_DEBUG_META }) .expect({ event: ANR_EVENT_WITH_DEBUG_META }) - .start(done); + .start() + .completed(); }); - test('blocked indefinitely', done => { - createRunner(__dirname, 'indefinite.mjs').withMockSentryServer().expect({ event: ANR_EVENT }).start(done); + test('blocked indefinitely', async () => { + await createRunner(__dirname, 'indefinite.mjs') + .withMockSentryServer() + .expect({ event: ANR_EVENT }) + .start() + .completed(); }); - test("With --inspect the debugger isn't used", done => { - createRunner(__dirname, 'basic.mjs') + test("With --inspect the debugger isn't used", async () => { + await createRunner(__dirname, 'basic.mjs') .withMockSentryServer() .withFlags('--inspect') .expect({ event: ANR_EVENT_WITHOUT_STACKTRACE }) - .start(done); + .start() + .completed(); }); - test('should exit', done => { + test('should exit', async () => { const runner = createRunner(__dirname, 'should-exit.js').start(); - setTimeout(() => { - expect(runner.childHasExited()).toBe(true); - done(); - }, 5_000); + await new Promise(resolve => setTimeout(resolve, 5_000)); + + expect(runner.childHasExited()).toBe(true); }); - test('should exit forced', done => { + test('should exit forced', async () => { const runner = createRunner(__dirname, 'should-exit-forced.js').start(); - setTimeout(() => { - expect(runner.childHasExited()).toBe(true); - done(); - }, 5_000); + await new Promise(resolve => setTimeout(resolve, 5_000)); + + expect(runner.childHasExited()).toBe(true); }); - test('With session', done => { - createRunner(__dirname, 'basic-session.js') + test('With session', async () => { + await createRunner(__dirname, 'basic-session.js') .withMockSentryServer() .unignore('session') .expect({ @@ -194,15 +205,16 @@ describe('should report ANR when event loop blocked', () => { }, }) .expect({ event: ANR_EVENT_WITH_SCOPE }) - .start(done); + .start() + .completed(); }); - test('from forked process', done => { - createRunner(__dirname, 'forker.js').expect({ event: ANR_EVENT_WITH_SCOPE }).start(done); + test('from forked process', async () => { + await createRunner(__dirname, 'forker.js').expect({ event: ANR_EVENT_WITH_SCOPE }).start().completed(); }); - test('worker can be stopped and restarted', done => { - createRunner(__dirname, 'stop-and-start.js').expect({ event: ANR_EVENT_WITH_SCOPE }).start(done); + test('worker can be stopped and restarted', async () => { + await createRunner(__dirname, 'stop-and-start.js').expect({ event: ANR_EVENT_WITH_SCOPE }).start().completed(); }); const EXPECTED_ISOLATED_EVENT = { @@ -231,10 +243,11 @@ describe('should report ANR when event loop blocked', () => { }, }; - test('fetches correct isolated scope', done => { - createRunner(__dirname, 'isolated.mjs') + test('fetches correct isolated scope', async () => { + await createRunner(__dirname, 'isolated.mjs') .withMockSentryServer() .expect({ event: EXPECTED_ISOLATED_EVENT }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/aws-serverless/aws-integration/s3/test.ts b/dev-packages/node-integration-tests/suites/aws-serverless/aws-integration/s3/test.ts index 90e2f94a9bed..b131eb421b61 100644 --- a/dev-packages/node-integration-tests/suites/aws-serverless/aws-integration/s3/test.ts +++ b/dev-packages/node-integration-tests/suites/aws-serverless/aws-integration/s3/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; const EXPECTED_TRANSCATION = { @@ -7,7 +8,7 @@ const EXPECTED_TRANSCATION = { description: 'S3.PutObject', op: 'rpc', origin: 'auto.otel.aws', - data: { + data: expect.objectContaining({ 'sentry.origin': 'auto.otel.aws', 'sentry.op': 'rpc', 'rpc.system': 'aws-api', @@ -16,7 +17,7 @@ const EXPECTED_TRANSCATION = { 'aws.region': 'us-east-1', 'aws.s3.bucket': 'ot-demo-test', 'otel.kind': 'CLIENT', - }, + }), }), ]), }; @@ -26,7 +27,11 @@ describe('awsIntegration', () => { cleanupChildProcesses(); }); - test('should auto-instrument aws-sdk v2 package.', done => { - createRunner(__dirname, 'scenario.js').ignore('event').expect({ transaction: EXPECTED_TRANSCATION }).start(done); + test('should auto-instrument aws-sdk v2 package.', async () => { + await createRunner(__dirname, 'scenario.js') + .ignore('event') + .expect({ transaction: EXPECTED_TRANSCATION }) + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/breadcrumbs/process-thread/test.ts b/dev-packages/node-integration-tests/suites/breadcrumbs/process-thread/test.ts index 632568fa85d1..a3ae49da4808 100644 --- a/dev-packages/node-integration-tests/suites/breadcrumbs/process-thread/test.ts +++ b/dev-packages/node-integration-tests/suites/breadcrumbs/process-thread/test.ts @@ -1,4 +1,5 @@ import type { Event } from '@sentry/core'; +import { afterAll, expect, test } from 'vitest'; import { conditionalTest } from '../../../utils'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; @@ -39,10 +40,11 @@ conditionalTest({ min: 20 })('should capture process and thread breadcrumbs', () cleanupChildProcesses(); }); - test('ESM', done => { - createRunner(__dirname, 'app.mjs') + test('ESM', async () => { + await createRunner(__dirname, 'app.mjs') .withMockSentryServer() .expect({ event: EVENT as Event }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/child-process/test.ts b/dev-packages/node-integration-tests/suites/child-process/test.ts index 9b9064dacf3e..798cd48d86d0 100644 --- a/dev-packages/node-integration-tests/suites/child-process/test.ts +++ b/dev-packages/node-integration-tests/suites/child-process/test.ts @@ -1,4 +1,5 @@ import type { Event } from '@sentry/core'; +import { afterAll, describe, expect, test } from 'vitest'; import { conditionalTest } from '../../utils'; import { cleanupChildProcesses, createRunner } from '../../utils/runner'; @@ -44,22 +45,22 @@ describe('should capture child process events', () => { }); conditionalTest({ min: 20 })('worker', () => { - test('ESM', done => { - createRunner(__dirname, 'worker.mjs').expect({ event: WORKER_EVENT }).start(done); + test('ESM', async () => { + await createRunner(__dirname, 'worker.mjs').expect({ event: WORKER_EVENT }).start().completed(); }); - test('CJS', done => { - createRunner(__dirname, 'worker.js').expect({ event: WORKER_EVENT }).start(done); + test('CJS', async () => { + await createRunner(__dirname, 'worker.js').expect({ event: WORKER_EVENT }).start().completed(); }); }); conditionalTest({ min: 20 })('fork', () => { - test('ESM', done => { - createRunner(__dirname, 'fork.mjs').expect({ event: CHILD_EVENT }).start(done); + test('ESM', async () => { + await createRunner(__dirname, 'fork.mjs').expect({ event: CHILD_EVENT }).start().completed(); }); - test('CJS', done => { - createRunner(__dirname, 'fork.js').expect({ event: CHILD_EVENT }).start(done); + test('CJS', async () => { + await createRunner(__dirname, 'fork.js').expect({ event: CHILD_EVENT }).start().completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/client-reports/drop-reasons/before-send/test.ts b/dev-packages/node-integration-tests/suites/client-reports/drop-reasons/before-send/test.ts index 06cac1581bfe..73a40fd88d17 100644 --- a/dev-packages/node-integration-tests/suites/client-reports/drop-reasons/before-send/test.ts +++ b/dev-packages/node-integration-tests/suites/client-reports/drop-reasons/before-send/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should record client report for beforeSend', done => { - createRunner(__dirname, 'scenario.ts') +test('should record client report for beforeSend', async () => { + await createRunner(__dirname, 'scenario.ts') .unignore('client_report') .expect({ client_report: { @@ -29,5 +30,6 @@ test('should record client report for beforeSend', done => { ], }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/client-reports/drop-reasons/event-processors/test.ts b/dev-packages/node-integration-tests/suites/client-reports/drop-reasons/event-processors/test.ts index 7dab2e904780..4e236e375c40 100644 --- a/dev-packages/node-integration-tests/suites/client-reports/drop-reasons/event-processors/test.ts +++ b/dev-packages/node-integration-tests/suites/client-reports/drop-reasons/event-processors/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should record client report for event processors', done => { - createRunner(__dirname, 'scenario.ts') +test('should record client report for event processors', async () => { + await createRunner(__dirname, 'scenario.ts') .unignore('client_report') .expect({ client_report: { @@ -29,5 +30,6 @@ test('should record client report for event processors', done => { ], }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/client-reports/periodic-send/test.ts b/dev-packages/node-integration-tests/suites/client-reports/periodic-send/test.ts index 65463193e1f5..69775219b784 100644 --- a/dev-packages/node-integration-tests/suites/client-reports/periodic-send/test.ts +++ b/dev-packages/node-integration-tests/suites/client-reports/periodic-send/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should flush client reports automatically after the timeout interval', done => { - createRunner(__dirname, 'scenario.ts') +test('should flush client reports automatically after the timeout interval', async () => { + await createRunner(__dirname, 'scenario.ts') .unignore('client_report') .expect({ client_report: { @@ -18,5 +19,6 @@ test('should flush client reports automatically after the timeout interval', don ], }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/contextLines/filename-with-spaces/test.ts b/dev-packages/node-integration-tests/suites/contextLines/filename-with-spaces/test.ts index 3407d1a14f9c..f469a585b9ca 100644 --- a/dev-packages/node-integration-tests/suites/contextLines/filename-with-spaces/test.ts +++ b/dev-packages/node-integration-tests/suites/contextLines/filename-with-spaces/test.ts @@ -1,12 +1,13 @@ import { join } from 'path'; +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../utils/runner'; describe('ContextLines integration in ESM', () => { - test('reads encoded context lines from filenames with spaces', done => { + test('reads encoded context lines from filenames with spaces', async () => { expect.assertions(1); const instrumentPath = join(__dirname, 'instrument.mjs'); - createRunner(__dirname, 'scenario with space.mjs') + await createRunner(__dirname, 'scenario with space.mjs') .withFlags('--import', instrumentPath) .expect({ event: { @@ -34,15 +35,16 @@ describe('ContextLines integration in ESM', () => { }, }, }) - .start(done); + .start() + .completed(); }); }); describe('ContextLines integration in CJS', () => { - test('reads context lines from filenames with spaces', done => { + test('reads context lines from filenames with spaces', async () => { expect.assertions(1); - createRunner(__dirname, 'scenario with space.cjs') + await createRunner(__dirname, 'scenario with space.cjs') .expect({ event: { exception: { @@ -77,6 +79,7 @@ describe('ContextLines integration in CJS', () => { }, }, }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/contextLines/memory-leak/test.ts b/dev-packages/node-integration-tests/suites/contextLines/memory-leak/test.ts index 0ec5ea95e896..c58a727e1f30 100644 --- a/dev-packages/node-integration-tests/suites/contextLines/memory-leak/test.ts +++ b/dev-packages/node-integration-tests/suites/contextLines/memory-leak/test.ts @@ -1,4 +1,5 @@ import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; +import { describe, afterAll, test } from 'vitest'; describe('ContextLines integration in CJS', () => { afterAll(() => { @@ -6,11 +7,12 @@ describe('ContextLines integration in CJS', () => { }); // Regression test for: https://github.com/getsentry/sentry-javascript/issues/14892 - test('does not leak open file handles', done => { - createRunner(__dirname, 'scenario.ts') + test('does not leak open file handles', async () => { + await createRunner(__dirname, 'scenario.ts') .expectN(10, { event: {}, }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/cron/cron/test.ts b/dev-packages/node-integration-tests/suites/cron/cron/test.ts index acd7815aebe1..8b9fdfd5c593 100644 --- a/dev-packages/node-integration-tests/suites/cron/cron/test.ts +++ b/dev-packages/node-integration-tests/suites/cron/cron/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('cron instrumentation', done => { - createRunner(__dirname, 'scenario.ts') +test('cron instrumentation', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ check_in: { check_in_id: expect.any(String), @@ -71,5 +72,6 @@ test('cron instrumentation', done => { exception: { values: [{ type: 'Error', value: 'Error in cron job' }] }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/cron/node-cron/test.ts b/dev-packages/node-integration-tests/suites/cron/node-cron/test.ts index 904da8180087..1c5fa515e208 100644 --- a/dev-packages/node-integration-tests/suites/cron/node-cron/test.ts +++ b/dev-packages/node-integration-tests/suites/cron/node-cron/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('node-cron instrumentation', done => { - createRunner(__dirname, 'scenario.ts') +test('node-cron instrumentation', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ check_in: { check_in_id: expect.any(String), @@ -71,5 +72,6 @@ test('node-cron instrumentation', done => { exception: { values: [{ type: 'Error', value: 'Error in cron job' }] }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/cron/node-schedule/test.ts b/dev-packages/node-integration-tests/suites/cron/node-schedule/test.ts index 74d815e56c88..a2019253203f 100644 --- a/dev-packages/node-integration-tests/suites/cron/node-schedule/test.ts +++ b/dev-packages/node-integration-tests/suites/cron/node-schedule/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('node-schedule instrumentation', done => { - createRunner(__dirname, 'scenario.ts') +test('node-schedule instrumentation', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ check_in: { check_in_id: expect.any(String), @@ -71,5 +72,6 @@ test('node-schedule instrumentation', done => { exception: { values: [{ type: 'Error', value: 'Error in cron job' }] }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/test.ts b/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/test.ts index 937edc76cd5c..99dea0e9193a 100644 --- a/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/test.ts +++ b/dev-packages/node-integration-tests/suites/esm/import-in-the-middle/test.ts @@ -1,5 +1,6 @@ import { spawnSync } from 'child_process'; import { join } from 'path'; +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses } from '../../../utils/runner'; afterAll(() => { diff --git a/dev-packages/node-integration-tests/suites/esm/modules-integration/test.ts b/dev-packages/node-integration-tests/suites/esm/modules-integration/test.ts index eaee003781f3..94995aedb91f 100644 --- a/dev-packages/node-integration-tests/suites/esm/modules-integration/test.ts +++ b/dev-packages/node-integration-tests/suites/esm/modules-integration/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { @@ -5,7 +6,7 @@ afterAll(() => { }); describe('modulesIntegration', () => { - test('does not crash ESM setups', done => { - createRunner(__dirname, 'app.mjs').ensureNoErrorOutput().start(done); + test('does not crash ESM setups', async () => { + await createRunner(__dirname, 'app.mjs').ensureNoErrorOutput().start().completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/esm/warn-esm/test.ts b/dev-packages/node-integration-tests/suites/esm/warn-esm/test.ts index f8d752497d46..41b3ce8f46f0 100644 --- a/dev-packages/node-integration-tests/suites/esm/warn-esm/test.ts +++ b/dev-packages/node-integration-tests/suites/esm/warn-esm/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { diff --git a/dev-packages/node-integration-tests/suites/express-v5/handle-error-scope-data-loss/test.ts b/dev-packages/node-integration-tests/suites/express-v5/handle-error-scope-data-loss/test.ts index 58d4a299174c..bd2f51c16dbd 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/handle-error-scope-data-loss/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/handle-error-scope-data-loss/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { @@ -13,8 +14,8 @@ afterAll(() => { * * This test nevertheless covers the behavior so that we're aware. */ -test('withScope scope is NOT applied to thrown error caught by global handler', done => { - createRunner(__dirname, 'server.ts') +test('withScope scope is NOT applied to thrown error caught by global handler', async () => { + const runner = createRunner(__dirname, 'server.ts') .expect({ event: { exception: { @@ -42,15 +43,16 @@ test('withScope scope is NOT applied to thrown error caught by global handler', tags: expect.not.objectContaining({ local: expect.anything() }), }, }) - .start(done) - .makeRequest('get', '/test/withScope', { expectError: true }); + .start(); + runner.makeRequest('get', '/test/withScope', { expectError: true }); + await runner.completed(); }); /** * This test shows that the isolation scope set tags are applied correctly to the error. */ -test('isolation scope is applied to thrown error caught by global handler', done => { - createRunner(__dirname, 'server.ts') +test('isolation scope is applied to thrown error caught by global handler', async () => { + const runner = createRunner(__dirname, 'server.ts') .expect({ event: { exception: { @@ -80,6 +82,7 @@ test('isolation scope is applied to thrown error caught by global handler', done }, }, }) - .start(done) - .makeRequest('get', '/test/isolationScope', { expectError: true }); + .start(); + runner.makeRequest('get', '/test/isolationScope', { expectError: true }); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-0/test.ts b/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-0/test.ts index 3ad6a3d2068f..b6bc5de97cdb 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-0/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-0/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should capture and send Express controller error with txn name if tracesSampleRate is 0', done => { - createRunner(__dirname, 'server.ts') +test('should capture and send Express controller error with txn name if tracesSampleRate is 0', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { @@ -33,6 +34,7 @@ test('should capture and send Express controller error with txn name if tracesSa transaction: 'GET /test/express/:id', }, }) - .start(done) - .makeRequest('get', '/test/express/123', { expectError: true }); + .start(); + runner.makeRequest('get', '/test/express/123', { expectError: true }); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-unset/test.ts b/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-unset/test.ts index b02d74016ad4..899a611cdfe0 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-unset/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/handle-error-tracesSampleRate-unset/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should capture and send Express controller error if tracesSampleRate is not set.', done => { - createRunner(__dirname, 'server.ts') +test('should capture and send Express controller error if tracesSampleRate is not set.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { @@ -32,6 +33,7 @@ test('should capture and send Express controller error if tracesSampleRate is no }, }, }) - .start(done) - .makeRequest('get', '/test/express/123', { expectError: true }); + .start(); + runner.makeRequest('get', '/test/express/123', { expectError: true }); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-init/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-init/test.ts index b80669a7c432..04d45fe557ef 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-init/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/multiple-init/test.ts @@ -1,10 +1,11 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('allows to call init multiple times', done => { +test('allows to call init multiple times', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -59,7 +60,7 @@ test('allows to call init multiple times', done => { }, }, }) - .start(done); + .start(); runner .makeRequest('get', '/test/no-init') @@ -67,4 +68,5 @@ test('allows to call init multiple times', done => { .then(() => runner.makeRequest('get', '/test/init')) .then(() => runner.makeRequest('get', '/test/error/2')) .then(() => runner.makeRequest('get', '/test/error/3')); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix-parameterized/test.ts index e7b9edabbfc8..85c3d0a2c353 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix-parameterized/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix-parameterized/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct url with common infixes with multiple parameterized routers.', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct url with common infixes with multiple parameterized routers.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/user/:userId' } }) - .start(done) - .makeRequest('get', '/api/v1/user/3212'); + .start(); + runner.makeRequest('get', '/api/v1/user/3212'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix/test.ts index 52d6b631bea2..f6a29574e254 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-infix/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct url with common infixes with multiple routers.', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct url with common infixes with multiple routers.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api2/v1/test' } }) - .start(done) - .makeRequest('get', '/api2/v1/test'); + .start(); + runner.makeRequest('get', '/api2/v1/test'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized-reverse/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized-reverse/test.ts index 5fabe5b92df6..b2b5baabd103 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized-reverse/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized-reverse/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct urls with multiple parameterized routers (use order reversed).', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct urls with multiple parameterized routers (use order reversed).', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/user/:userId' } }) - .start(done) - .makeRequest('get', '/api/v1/user/1234/'); + .start(); + runner.makeRequest('get', '/api/v1/user/1234/'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized/test.ts index bab934f54522..f362d49ddd03 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-parameterized/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct urls with multiple parameterized routers.', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct urls with multiple parameterized routers.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/user/:userId' } }) - .start(done) - .makeRequest('get', '/api/v1/user/1234/'); + .start(); + runner.makeRequest('get', '/api/v1/user/1234/'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized copy/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized copy/test.ts index 94d363f4faa4..146e7d0625c5 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized copy/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized copy/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct url with multiple parameterized routers of the same length (use order reversed).', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct url with multiple parameterized routers of the same length (use order reversed).', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/:userId' } }) - .start(done) - .makeRequest('get', '/api/v1/1234/'); + .start(); + runner.makeRequest('get', '/api/v1/1234/'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized/test.ts index 373b2c102c4c..3209cde3ea23 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix-same-length-parameterized/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct url with multiple parameterized routers of the same length.', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct url with multiple parameterized routers of the same length.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/:userId' } }) - .start(done) - .makeRequest('get', '/api/v1/1234/'); + .start(); + runner.makeRequest('get', '/api/v1/1234/'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix/test.ts index ea217bf6bc05..12c6584b2d3f 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/common-prefix/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct urls with multiple routers.', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct urls with multiple routers.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/test' } }) - .start(done) - .makeRequest('get', '/api/v1/test'); + .start(); + runner.makeRequest('get', '/api/v1/test'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/complex-router/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/complex-router/test.ts index fe065d0dc550..921017909498 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/complex-router/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/complex-router/test.ts @@ -1,3 +1,4 @@ +import { afterAll, test, describe } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { @@ -5,7 +6,7 @@ afterAll(() => { }); describe('complex-router', () => { - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route', done => { + test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /api/api/v1/sub-router/users/:userId/posts/:postId', transaction_info: { @@ -13,14 +14,15 @@ describe('complex-router', () => { }, }; - createRunner(__dirname, 'server.ts') + const runner = createRunner(__dirname, 'server.ts') .ignore('event') .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(done) - .makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456'); + .start(); + runner.makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456'); + await runner.completed(); }); - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route and original url has query params', done => { + test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route and original url has query params', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /api/api/v1/sub-router/users/:userId/posts/:postId', transaction_info: { @@ -28,14 +30,15 @@ describe('complex-router', () => { }, }; - createRunner(__dirname, 'server.ts') + const runner = createRunner(__dirname, 'server.ts') .ignore('event') .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(done) - .makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456?param=1'); + .start(); + runner.makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456?param=1'); + await runner.completed(); }); - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route and original url ends with trailing slash and has query params', done => { + test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route and original url ends with trailing slash and has query params', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /api/api/v1/sub-router/users/:userId/posts/:postId', transaction_info: { @@ -43,10 +46,11 @@ describe('complex-router', () => { }, }; - createRunner(__dirname, 'server.ts') + const runner = createRunner(__dirname, 'server.ts') .ignore('event') .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(done) - .makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456/?param=1'); + .start(); + runner.makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456/?param=1'); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/middle-layer-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/middle-layer-parameterized/test.ts index 52a6ce154684..6f24e03bac59 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/middle-layer-parameterized/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/multiple-routers/middle-layer-parameterized/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { @@ -6,7 +7,7 @@ afterAll(() => { // Before Node 16, parametrization is not working properly here describe('middle-layer-parameterized', () => { - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route', done => { + test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /api/v1/users/:userId/posts/:postId', transaction_info: { @@ -14,10 +15,11 @@ describe('middle-layer-parameterized', () => { }, }; - createRunner(__dirname, 'server.ts') + const runner = createRunner(__dirname, 'server.ts') .ignore('event') .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(done) - .makeRequest('get', '/api/v1/users/123/posts/456'); + .start(); + runner.makeRequest('get', '/api/v1/users/123/posts/456'); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/requestUser/test.ts b/dev-packages/node-integration-tests/suites/express-v5/requestUser/test.ts index 2a9fc58a7c18..2605b7ed5127 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/requestUser/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/requestUser/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('express user handling', () => { @@ -5,22 +6,23 @@ describe('express user handling', () => { cleanupChildProcesses(); }); - test('ignores user from request', done => { + test('ignores user from request', async () => { expect.assertions(2); - createRunner(__dirname, 'server.js') + const runner = createRunner(__dirname, 'server.js') .expect({ event: event => { expect(event.user).toBeUndefined(); expect(event.exception?.values?.[0]?.value).toBe('error_1'); }, }) - .start(done) - .makeRequest('get', '/test1', { expectError: true }); + .start(); + runner.makeRequest('get', '/test1', { expectError: true }); + await runner.completed(); }); - test('using setUser in middleware works', done => { - createRunner(__dirname, 'server.js') + test('using setUser in middleware works', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ event: { user: { @@ -36,7 +38,8 @@ describe('express user handling', () => { }, }, }) - .start(done) - .makeRequest('get', '/test2', { expectError: true }); + .start(); + runner.makeRequest('get', '/test2', { expectError: true }); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-assign/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-assign/test.ts index 513cf6146d0f..8b8d648513e4 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-assign/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-assign/test.ts @@ -1,4 +1,5 @@ import { parseBaggageHeader } from '@sentry/core'; +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from '../server'; diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-out/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-out/test.ts index 72b6a7139f35..913fcd5e2038 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-out/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-header-out/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from './server'; diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts index ebf2a15bedf4..2d2074be773c 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from '../server'; diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors/test.ts index 0beecb54a905..beb118944408 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-other-vendors/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from './server'; diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-transaction-name/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-transaction-name/test.ts index 1001d0839aea..436a8ea9b4da 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-transaction-name/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/baggage-transaction-name/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from '../server'; diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-assign/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-assign/test.ts index 40bbb03f8d50..7d0a729dc4ff 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-assign/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-assign/test.ts @@ -1,4 +1,5 @@ import { TRACEPARENT_REGEXP } from '@sentry/core'; +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from '../server'; diff --git a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-out/test.ts b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-out/test.ts index db46bb491904..8ed4d08bba55 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-out/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/sentry-trace/trace-header-out/test.ts @@ -1,4 +1,5 @@ import { TRACEPARENT_REGEXP } from '@sentry/core'; +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from '../server'; diff --git a/dev-packages/node-integration-tests/suites/express-v5/setupExpressErrorHandler/test.ts b/dev-packages/node-integration-tests/suites/express-v5/setupExpressErrorHandler/test.ts index ffc702d63057..571ffc52e224 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/setupExpressErrorHandler/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/setupExpressErrorHandler/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('express setupExpressErrorHandler', () => { @@ -6,7 +7,7 @@ describe('express setupExpressErrorHandler', () => { }); describe('CJS', () => { - test('allows to pass options to setupExpressErrorHandler', done => { + test('allows to pass options to setupExpressErrorHandler', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ event: { @@ -19,12 +20,13 @@ describe('express setupExpressErrorHandler', () => { }, }, }) - .start(done); + .start(); // this error is filtered & ignored runner.makeRequest('get', '/test1', { expectError: true }); // this error is actually captured runner.makeRequest('get', '/test2', { expectError: true }); + await runner.completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/span-isolationScope/test.ts b/dev-packages/node-integration-tests/suites/express-v5/span-isolationScope/test.ts index 2e2b6945526e..f8c7c11378d5 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/span-isolationScope/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/span-isolationScope/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('correctly applies isolation scope to span', done => { - createRunner(__dirname, 'server.ts') +test('correctly applies isolation scope to span', async () => { + const runner = createRunner(__dirname, 'server.ts') .expect({ transaction: { transaction: 'GET /test/isolationScope', @@ -33,6 +34,7 @@ test('correctly applies isolation scope to span', done => { }, }, }) - .start(done) - .makeRequest('get', '/test/isolationScope'); + .start(); + runner.makeRequest('get', '/test/isolationScope'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/test.ts b/dev-packages/node-integration-tests/suites/express-v5/tracing/test.ts index 6f87fdd89f76..cf20276f4fc2 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/tracing/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('express tracing', () => { @@ -6,8 +7,8 @@ describe('express tracing', () => { }); describe('CJS', () => { - test('should create and send transactions for Express routes and spans for middlewares.', done => { - createRunner(__dirname, 'server.js') + test('should create and send transactions for Express routes and spans for middlewares.', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { contexts: { @@ -44,12 +45,13 @@ describe('express tracing', () => { ]), }, }) - .start(done) - .makeRequest('get', '/test/express'); + .start(); + runner.makeRequest('get', '/test/express'); + await runner.completed(); }); - test('should set a correct transaction name for routes specified in RegEx', done => { - createRunner(__dirname, 'server.js') + test('should set a correct transaction name for routes specified in RegEx', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { transaction: 'GET /\\/test\\/regex/', @@ -70,8 +72,9 @@ describe('express tracing', () => { }, }, }) - .start(done) - .makeRequest('get', '/test/regex'); + .start(); + runner.makeRequest('get', '/test/regex'); + await runner.completed(); }); test.each([['array1'], ['array5']])( @@ -137,7 +140,7 @@ describe('express tracing', () => { }) as any); describe('request data', () => { - test('correctly captures JSON request data', done => { + test('correctly captures JSON request data', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { @@ -156,12 +159,13 @@ describe('express tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { data: { foo: 'bar', other: 1 } }); + await runner.completed(); }); - test('correctly captures plain text request data', done => { + test('correctly captures plain text request data', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { @@ -177,15 +181,16 @@ describe('express tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { headers: { 'Content-Type': 'text/plain' }, data: 'some plain text', }); + await runner.completed(); }); - test('correctly captures text buffer request data', done => { + test('correctly captures text buffer request data', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { @@ -201,15 +206,16 @@ describe('express tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { headers: { 'Content-Type': 'application/octet-stream' }, data: Buffer.from('some plain text in buffer'), }); + await runner.completed(); }); - test('correctly captures non-text buffer request data', done => { + test('correctly captures non-text buffer request data', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { @@ -226,7 +232,7 @@ describe('express tracing', () => { }, }, }) - .start(done); + .start(); const body = new Uint8Array([1, 2, 3, 4, 5]).buffer; @@ -234,6 +240,7 @@ describe('express tracing', () => { headers: { 'Content-Type': 'application/octet-stream' }, data: body, }); + await runner.completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/test.ts b/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/test.ts index 07cc8d094d8f..1b644ada387a 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/tracing/tracesSampler/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('express tracesSampler', () => { @@ -6,19 +7,20 @@ describe('express tracesSampler', () => { }); describe('CJS', () => { - test('correctly samples & passes data to tracesSampler', done => { + test('correctly samples & passes data to tracesSampler', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { transaction: 'GET /test/:id', }, }) - .start(done); + .start(); // This is not sampled runner.makeRequest('get', '/test2?q=1'); // This is sampled runner.makeRequest('get', '/test/123?q=1'); + await runner.completed(); }); }); }); @@ -29,16 +31,17 @@ describe('express tracesSampler includes normalizedRequest data', () => { }); describe('CJS', () => { - test('correctly samples & passes data to tracesSampler', done => { + test('correctly samples & passes data to tracesSampler', async () => { const runner = createRunner(__dirname, 'scenario-normalizedRequest.js') .expect({ transaction: { transaction: 'GET /test-normalized-request', }, }) - .start(done); + .start(); runner.makeRequest('get', '/test-normalized-request?query=123'); + await runner.completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/updateName/test.ts b/dev-packages/node-integration-tests/suites/express-v5/tracing/updateName/test.ts index c6345713fd7e..227cf6042c44 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/updateName/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/tracing/updateName/test.ts @@ -1,5 +1,6 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME } from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/node'; +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('express tracing', () => { @@ -11,8 +12,8 @@ describe('express tracing', () => { // This test documents the unfortunate behaviour of using `span.updateName` on the server-side. // For http.server root spans (which is the root span on the server 99% of the time), Otel's http instrumentation // calls `span.updateName` and overwrites whatever the name was set to before (by us or by users). - test("calling just `span.updateName` doesn't update the final name in express (missing source)", done => { - createRunner(__dirname, 'server.js') + test("calling just `span.updateName` doesn't update the final name in express (missing source)", async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { transaction: 'GET /test/:id/span-updateName', @@ -21,14 +22,15 @@ describe('express tracing', () => { }, }, }) - .start(done) - .makeRequest('get', '/test/123/span-updateName'); + .start(); + runner.makeRequest('get', '/test/123/span-updateName'); + await runner.completed(); }); // Also calling `updateName` AND setting a source doesn't change anything - Otel has no concept of source, this is sentry-internal. // Therefore, only the source is updated but the name is still overwritten by Otel. - test("calling `span.updateName` and setting attribute source doesn't update the final name in express but it updates the source", done => { - createRunner(__dirname, 'server.js') + test("calling `span.updateName` and setting attribute source doesn't update the final name in express but it updates the source", async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { transaction: 'GET /test/:id/span-updateName-source', @@ -37,13 +39,14 @@ describe('express tracing', () => { }, }, }) - .start(done) - .makeRequest('get', '/test/123/span-updateName-source'); + .start(); + runner.makeRequest('get', '/test/123/span-updateName-source'); + await runner.completed(); }); // This test documents the correct way to update the span name (and implicitly the source) in Node: - test('calling `Sentry.updateSpanName` updates the final name and source in express', done => { - createRunner(__dirname, 'server.js') + test('calling `Sentry.updateSpanName` updates the final name and source in express', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: txnEvent => { expect(txnEvent).toMatchObject({ @@ -62,14 +65,15 @@ describe('express tracing', () => { expect(txnEvent.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toBeUndefined(); }, }) - .start(done) - .makeRequest('get', '/test/123/updateSpanName'); + .start(); + runner.makeRequest('get', '/test/123/updateSpanName'); + await runner.completed(); }); }); // This test documents the correct way to update the span name (and implicitly the source) in Node: - test('calling `Sentry.updateSpanName` and setting source subsequently updates the final name and sets correct source', done => { - createRunner(__dirname, 'server.js') + test('calling `Sentry.updateSpanName` and setting source subsequently updates the final name and sets correct source', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: txnEvent => { expect(txnEvent).toMatchObject({ @@ -88,7 +92,8 @@ describe('express tracing', () => { expect(txnEvent.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toBeUndefined(); }, }) - .start(done) - .makeRequest('get', '/test/123/updateSpanNameAndSource'); + .start(); + runner.makeRequest('get', '/test/123/updateSpanNameAndSource'); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/tracing/withError/test.ts b/dev-packages/node-integration-tests/suites/express-v5/tracing/withError/test.ts index 4dd004ad2239..e99c5bf44700 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/tracing/withError/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/tracing/withError/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('express tracing experimental', () => { @@ -6,8 +7,8 @@ describe('express tracing experimental', () => { }); describe('CJS', () => { - test('should apply the scope transactionName to error events', done => { - createRunner(__dirname, 'server.js') + test('should apply the scope transactionName to error events', async () => { + const runner = createRunner(__dirname, 'server.js') .ignore('transaction') .expect({ event: { @@ -21,8 +22,9 @@ describe('express tracing experimental', () => { transaction: 'GET /test/:id1/:id2', }, }) - .start(done) - .makeRequest('get', '/test/123/abc?q=1'); + .start(); + runner.makeRequest('get', '/test/123/abc?q=1'); + await runner.completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/express-v5/without-tracing/test.ts b/dev-packages/node-integration-tests/suites/express-v5/without-tracing/test.ts index fdd63ad4aa4b..5dc6aedecbd4 100644 --- a/dev-packages/node-integration-tests/suites/express-v5/without-tracing/test.ts +++ b/dev-packages/node-integration-tests/suites/express-v5/without-tracing/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { @@ -5,7 +6,7 @@ afterAll(() => { }); describe('express without tracing', () => { - test('correctly applies isolation scope even without tracing', done => { + test('correctly applies isolation scope even without tracing', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -25,13 +26,14 @@ describe('express without tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('get', '/test/isolationScope/1'); + await runner.completed(); }); describe('request data', () => { - test('correctly captures JSON request data', done => { + test('correctly captures JSON request data', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -50,12 +52,13 @@ describe('express without tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { data: { foo: 'bar', other: 1 } }); + await runner.completed(); }); - test('correctly captures plain text request data', done => { + test('correctly captures plain text request data', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -71,7 +74,7 @@ describe('express without tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { headers: { @@ -79,9 +82,10 @@ describe('express without tracing', () => { }, data: 'some plain text', }); + await runner.completed(); }); - test('correctly captures text buffer request data', done => { + test('correctly captures text buffer request data', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -97,15 +101,16 @@ describe('express without tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { headers: { 'Content-Type': 'application/octet-stream' }, data: Buffer.from('some plain text in buffer'), }); + await runner.completed(); }); - test('correctly captures non-text buffer request data', done => { + test('correctly captures non-text buffer request data', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -122,11 +127,12 @@ describe('express without tracing', () => { }, }, }) - .start(done); + .start(); const body = new Uint8Array([1, 2, 3, 4, 5]).buffer; runner.makeRequest('post', '/test-post', { headers: { 'Content-Type': 'application/octet-stream' }, data: body }); + await runner.completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/express/handle-error-scope-data-loss/test.ts b/dev-packages/node-integration-tests/suites/express/handle-error-scope-data-loss/test.ts index 58d4a299174c..eda622f1cf6c 100644 --- a/dev-packages/node-integration-tests/suites/express/handle-error-scope-data-loss/test.ts +++ b/dev-packages/node-integration-tests/suites/express/handle-error-scope-data-loss/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { @@ -13,8 +14,8 @@ afterAll(() => { * * This test nevertheless covers the behavior so that we're aware. */ -test('withScope scope is NOT applied to thrown error caught by global handler', done => { - createRunner(__dirname, 'server.ts') +test('withScope scope is NOT applied to thrown error caught by global handler', async () => { + const runner = createRunner(__dirname, 'server.ts') .expect({ event: { exception: { @@ -42,15 +43,18 @@ test('withScope scope is NOT applied to thrown error caught by global handler', tags: expect.not.objectContaining({ local: expect.anything() }), }, }) - .start(done) - .makeRequest('get', '/test/withScope', { expectError: true }); + .start(); + + runner.makeRequest('get', '/test/withScope', { expectError: true }); + + await runner.completed(); }); /** * This test shows that the isolation scope set tags are applied correctly to the error. */ -test('isolation scope is applied to thrown error caught by global handler', done => { - createRunner(__dirname, 'server.ts') +test('isolation scope is applied to thrown error caught by global handler', async () => { + const runner = createRunner(__dirname, 'server.ts') .expect({ event: { exception: { @@ -80,6 +84,9 @@ test('isolation scope is applied to thrown error caught by global handler', done }, }, }) - .start(done) - .makeRequest('get', '/test/isolationScope', { expectError: true }); + .start(); + + runner.makeRequest('get', '/test/isolationScope', { expectError: true }); + + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-0/test.ts b/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-0/test.ts index 3ad6a3d2068f..b6bc5de97cdb 100644 --- a/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-0/test.ts +++ b/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-0/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should capture and send Express controller error with txn name if tracesSampleRate is 0', done => { - createRunner(__dirname, 'server.ts') +test('should capture and send Express controller error with txn name if tracesSampleRate is 0', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { @@ -33,6 +34,7 @@ test('should capture and send Express controller error with txn name if tracesSa transaction: 'GET /test/express/:id', }, }) - .start(done) - .makeRequest('get', '/test/express/123', { expectError: true }); + .start(); + runner.makeRequest('get', '/test/express/123', { expectError: true }); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-unset/test.ts b/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-unset/test.ts index b02d74016ad4..4f0dcaae1411 100644 --- a/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-unset/test.ts +++ b/dev-packages/node-integration-tests/suites/express/handle-error-tracesSampleRate-unset/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should capture and send Express controller error if tracesSampleRate is not set.', done => { - createRunner(__dirname, 'server.ts') +test('should capture and send Express controller error if tracesSampleRate is not set.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { @@ -32,6 +33,8 @@ test('should capture and send Express controller error if tracesSampleRate is no }, }, }) - .start(done) - .makeRequest('get', '/test/express/123', { expectError: true }); + .start(); + + runner.makeRequest('get', '/test/express/123', { expectError: true }); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/multiple-init/test.ts b/dev-packages/node-integration-tests/suites/express/multiple-init/test.ts index b80669a7c432..7ab331485e7d 100644 --- a/dev-packages/node-integration-tests/suites/express/multiple-init/test.ts +++ b/dev-packages/node-integration-tests/suites/express/multiple-init/test.ts @@ -1,10 +1,11 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('allows to call init multiple times', done => { +test('allows to call init multiple times', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -59,7 +60,7 @@ test('allows to call init multiple times', done => { }, }, }) - .start(done); + .start(); runner .makeRequest('get', '/test/no-init') @@ -67,4 +68,6 @@ test('allows to call init multiple times', done => { .then(() => runner.makeRequest('get', '/test/init')) .then(() => runner.makeRequest('get', '/test/error/2')) .then(() => runner.makeRequest('get', '/test/error/3')); + + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-infix-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-infix-parameterized/test.ts index e7b9edabbfc8..85c3d0a2c353 100644 --- a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-infix-parameterized/test.ts +++ b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-infix-parameterized/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct url with common infixes with multiple parameterized routers.', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct url with common infixes with multiple parameterized routers.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/user/:userId' } }) - .start(done) - .makeRequest('get', '/api/v1/user/3212'); + .start(); + runner.makeRequest('get', '/api/v1/user/3212'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-infix/test.ts b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-infix/test.ts index 52d6b631bea2..f6a29574e254 100644 --- a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-infix/test.ts +++ b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-infix/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct url with common infixes with multiple routers.', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct url with common infixes with multiple routers.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api2/v1/test' } }) - .start(done) - .makeRequest('get', '/api2/v1/test'); + .start(); + runner.makeRequest('get', '/api2/v1/test'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-parameterized-reverse/test.ts b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-parameterized-reverse/test.ts index 5fabe5b92df6..b2b5baabd103 100644 --- a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-parameterized-reverse/test.ts +++ b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-parameterized-reverse/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct urls with multiple parameterized routers (use order reversed).', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct urls with multiple parameterized routers (use order reversed).', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/user/:userId' } }) - .start(done) - .makeRequest('get', '/api/v1/user/1234/'); + .start(); + runner.makeRequest('get', '/api/v1/user/1234/'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-parameterized/test.ts index bab934f54522..f362d49ddd03 100644 --- a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-parameterized/test.ts +++ b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-parameterized/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct urls with multiple parameterized routers.', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct urls with multiple parameterized routers.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/user/:userId' } }) - .start(done) - .makeRequest('get', '/api/v1/user/1234/'); + .start(); + runner.makeRequest('get', '/api/v1/user/1234/'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-same-length-parameterized copy/test.ts b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-same-length-parameterized copy/test.ts index 94d363f4faa4..146e7d0625c5 100644 --- a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-same-length-parameterized copy/test.ts +++ b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-same-length-parameterized copy/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct url with multiple parameterized routers of the same length (use order reversed).', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct url with multiple parameterized routers of the same length (use order reversed).', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/:userId' } }) - .start(done) - .makeRequest('get', '/api/v1/1234/'); + .start(); + runner.makeRequest('get', '/api/v1/1234/'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-same-length-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-same-length-parameterized/test.ts index 373b2c102c4c..3209cde3ea23 100644 --- a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-same-length-parameterized/test.ts +++ b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix-same-length-parameterized/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct url with multiple parameterized routers of the same length.', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct url with multiple parameterized routers of the same length.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/:userId' } }) - .start(done) - .makeRequest('get', '/api/v1/1234/'); + .start(); + runner.makeRequest('get', '/api/v1/1234/'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix/test.ts b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix/test.ts index ea217bf6bc05..12c6584b2d3f 100644 --- a/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix/test.ts +++ b/dev-packages/node-integration-tests/suites/express/multiple-routers/common-prefix/test.ts @@ -1,13 +1,15 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should construct correct urls with multiple routers.', done => { - createRunner(__dirname, 'server.ts') +test('should construct correct urls with multiple routers.', async () => { + const runner = createRunner(__dirname, 'server.ts') .ignore('transaction') .expect({ event: { message: 'Custom Message', transaction: 'GET /api/v1/test' } }) - .start(done) - .makeRequest('get', '/api/v1/test'); + .start(); + runner.makeRequest('get', '/api/v1/test'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/multiple-routers/complex-router/test.ts b/dev-packages/node-integration-tests/suites/express/multiple-routers/complex-router/test.ts index fe065d0dc550..fbb97cb6b1df 100644 --- a/dev-packages/node-integration-tests/suites/express/multiple-routers/complex-router/test.ts +++ b/dev-packages/node-integration-tests/suites/express/multiple-routers/complex-router/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { @@ -5,7 +6,7 @@ afterAll(() => { }); describe('complex-router', () => { - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route', done => { + test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /api/api/v1/sub-router/users/:userId/posts/:postId', transaction_info: { @@ -13,14 +14,15 @@ describe('complex-router', () => { }, }; - createRunner(__dirname, 'server.ts') + const runner = createRunner(__dirname, 'server.ts') .ignore('event') .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(done) - .makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456'); + .start(); + runner.makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456'); + await runner.completed(); }); - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route and original url has query params', done => { + test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route and original url has query params', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /api/api/v1/sub-router/users/:userId/posts/:postId', transaction_info: { @@ -28,14 +30,15 @@ describe('complex-router', () => { }, }; - createRunner(__dirname, 'server.ts') + const runner = createRunner(__dirname, 'server.ts') .ignore('event') .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(done) - .makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456?param=1'); + .start(); + runner.makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456?param=1'); + await runner.completed(); }); - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route and original url ends with trailing slash and has query params', done => { + test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route and express used multiple middlewares with route and original url ends with trailing slash and has query params', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /api/api/v1/sub-router/users/:userId/posts/:postId', transaction_info: { @@ -43,10 +46,11 @@ describe('complex-router', () => { }, }; - createRunner(__dirname, 'server.ts') + const runner = createRunner(__dirname, 'server.ts') .ignore('event') .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(done) - .makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456/?param=1'); + .start(); + runner.makeRequest('get', '/api/api/v1/sub-router/users/123/posts/456/?param=1'); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/express/multiple-routers/middle-layer-parameterized/test.ts b/dev-packages/node-integration-tests/suites/express/multiple-routers/middle-layer-parameterized/test.ts index 52a6ce154684..6f24e03bac59 100644 --- a/dev-packages/node-integration-tests/suites/express/multiple-routers/middle-layer-parameterized/test.ts +++ b/dev-packages/node-integration-tests/suites/express/multiple-routers/middle-layer-parameterized/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { @@ -6,7 +7,7 @@ afterAll(() => { // Before Node 16, parametrization is not working properly here describe('middle-layer-parameterized', () => { - test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route', done => { + test('should construct correct url with multiple parameterized routers, when param is also contain in middle layer route', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /api/v1/users/:userId/posts/:postId', transaction_info: { @@ -14,10 +15,11 @@ describe('middle-layer-parameterized', () => { }, }; - createRunner(__dirname, 'server.ts') + const runner = createRunner(__dirname, 'server.ts') .ignore('event') .expect({ transaction: EXPECTED_TRANSACTION as any }) - .start(done) - .makeRequest('get', '/api/v1/users/123/posts/456'); + .start(); + runner.makeRequest('get', '/api/v1/users/123/posts/456'); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/express/requestUser/test.ts b/dev-packages/node-integration-tests/suites/express/requestUser/test.ts index 2a9fc58a7c18..2605b7ed5127 100644 --- a/dev-packages/node-integration-tests/suites/express/requestUser/test.ts +++ b/dev-packages/node-integration-tests/suites/express/requestUser/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('express user handling', () => { @@ -5,22 +6,23 @@ describe('express user handling', () => { cleanupChildProcesses(); }); - test('ignores user from request', done => { + test('ignores user from request', async () => { expect.assertions(2); - createRunner(__dirname, 'server.js') + const runner = createRunner(__dirname, 'server.js') .expect({ event: event => { expect(event.user).toBeUndefined(); expect(event.exception?.values?.[0]?.value).toBe('error_1'); }, }) - .start(done) - .makeRequest('get', '/test1', { expectError: true }); + .start(); + runner.makeRequest('get', '/test1', { expectError: true }); + await runner.completed(); }); - test('using setUser in middleware works', done => { - createRunner(__dirname, 'server.js') + test('using setUser in middleware works', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ event: { user: { @@ -36,7 +38,8 @@ describe('express user handling', () => { }, }, }) - .start(done) - .makeRequest('get', '/test2', { expectError: true }); + .start(); + runner.makeRequest('get', '/test2', { expectError: true }); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts b/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts index 513cf6146d0f..8b8d648513e4 100644 --- a/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts +++ b/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-header-assign/test.ts @@ -1,4 +1,5 @@ import { parseBaggageHeader } from '@sentry/core'; +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from '../server'; diff --git a/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts b/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts index 72b6a7139f35..913fcd5e2038 100644 --- a/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts +++ b/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-header-out/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from './server'; diff --git a/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts b/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts index ebf2a15bedf4..2d2074be773c 100644 --- a/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts +++ b/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors-with-sentry-entries/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from '../server'; diff --git a/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/test.ts b/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/test.ts index 0beecb54a905..beb118944408 100644 --- a/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/test.ts +++ b/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-other-vendors/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from './server'; diff --git a/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/test.ts b/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/test.ts index 1001d0839aea..436a8ea9b4da 100644 --- a/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/test.ts +++ b/dev-packages/node-integration-tests/suites/express/sentry-trace/baggage-transaction-name/test.ts @@ -1,3 +1,4 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from '../server'; diff --git a/dev-packages/node-integration-tests/suites/express/sentry-trace/trace-header-assign/test.ts b/dev-packages/node-integration-tests/suites/express/sentry-trace/trace-header-assign/test.ts index 40bbb03f8d50..7d0a729dc4ff 100644 --- a/dev-packages/node-integration-tests/suites/express/sentry-trace/trace-header-assign/test.ts +++ b/dev-packages/node-integration-tests/suites/express/sentry-trace/trace-header-assign/test.ts @@ -1,4 +1,5 @@ import { TRACEPARENT_REGEXP } from '@sentry/core'; +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from '../server'; diff --git a/dev-packages/node-integration-tests/suites/express/sentry-trace/trace-header-out/test.ts b/dev-packages/node-integration-tests/suites/express/sentry-trace/trace-header-out/test.ts index db46bb491904..8ed4d08bba55 100644 --- a/dev-packages/node-integration-tests/suites/express/sentry-trace/trace-header-out/test.ts +++ b/dev-packages/node-integration-tests/suites/express/sentry-trace/trace-header-out/test.ts @@ -1,4 +1,5 @@ import { TRACEPARENT_REGEXP } from '@sentry/core'; +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; import type { TestAPIResponse } from '../server'; diff --git a/dev-packages/node-integration-tests/suites/express/setupExpressErrorHandler/test.ts b/dev-packages/node-integration-tests/suites/express/setupExpressErrorHandler/test.ts index ffc702d63057..cb76cf3c929b 100644 --- a/dev-packages/node-integration-tests/suites/express/setupExpressErrorHandler/test.ts +++ b/dev-packages/node-integration-tests/suites/express/setupExpressErrorHandler/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('express setupExpressErrorHandler', () => { @@ -6,7 +7,7 @@ describe('express setupExpressErrorHandler', () => { }); describe('CJS', () => { - test('allows to pass options to setupExpressErrorHandler', done => { + test('allows to pass options to setupExpressErrorHandler', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ event: { @@ -19,12 +20,14 @@ describe('express setupExpressErrorHandler', () => { }, }, }) - .start(done); + .start(); // this error is filtered & ignored runner.makeRequest('get', '/test1', { expectError: true }); // this error is actually captured runner.makeRequest('get', '/test2', { expectError: true }); + + await runner.completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/express/span-isolationScope/test.ts b/dev-packages/node-integration-tests/suites/express/span-isolationScope/test.ts index 2e2b6945526e..f8c7c11378d5 100644 --- a/dev-packages/node-integration-tests/suites/express/span-isolationScope/test.ts +++ b/dev-packages/node-integration-tests/suites/express/span-isolationScope/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('correctly applies isolation scope to span', done => { - createRunner(__dirname, 'server.ts') +test('correctly applies isolation scope to span', async () => { + const runner = createRunner(__dirname, 'server.ts') .expect({ transaction: { transaction: 'GET /test/isolationScope', @@ -33,6 +34,7 @@ test('correctly applies isolation scope to span', done => { }, }, }) - .start(done) - .makeRequest('get', '/test/isolationScope'); + .start(); + runner.makeRequest('get', '/test/isolationScope'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/express/tracing/test.ts b/dev-packages/node-integration-tests/suites/express/tracing/test.ts index 1c1d8439822d..ebf9977e722b 100644 --- a/dev-packages/node-integration-tests/suites/express/tracing/test.ts +++ b/dev-packages/node-integration-tests/suites/express/tracing/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('express tracing', () => { @@ -6,8 +7,8 @@ describe('express tracing', () => { }); describe('CJS', () => { - test('should create and send transactions for Express routes and spans for middlewares.', done => { - createRunner(__dirname, 'server.js') + test('should create and send transactions for Express routes and spans for middlewares.', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { contexts: { @@ -44,12 +45,13 @@ describe('express tracing', () => { ]), }, }) - .start(done) - .makeRequest('get', '/test/express'); + .start(); + runner.makeRequest('get', '/test/express'); + await runner.completed(); }); - test('should set a correct transaction name for routes specified in RegEx', done => { - createRunner(__dirname, 'server.js') + test('should set a correct transaction name for routes specified in RegEx', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { transaction: 'GET /\\/test\\/regex/', @@ -70,8 +72,9 @@ describe('express tracing', () => { }, }, }) - .start(done) - .makeRequest('get', '/test/regex'); + .start(); + runner.makeRequest('get', '/test/regex'); + await runner.completed(); }); test.each([['array1'], ['array5']])( @@ -139,7 +142,7 @@ describe('express tracing', () => { }) as any); describe('request data', () => { - test('correctly captures JSON request data', done => { + test('correctly captures JSON request data', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { @@ -158,12 +161,13 @@ describe('express tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { data: { foo: 'bar', other: 1 } }); + await runner.completed(); }); - test('correctly captures plain text request data', done => { + test('correctly captures plain text request data', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { @@ -179,15 +183,16 @@ describe('express tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { headers: { 'Content-Type': 'text/plain' }, data: 'some plain text', }); + await runner.completed(); }); - test('correctly captures text buffer request data', done => { + test('correctly captures text buffer request data', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { @@ -203,15 +208,16 @@ describe('express tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { headers: { 'Content-Type': 'application/octet-stream' }, data: Buffer.from('some plain text in buffer'), }); + await runner.completed(); }); - test('correctly captures non-text buffer request data', done => { + test('correctly captures non-text buffer request data', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { @@ -228,7 +234,7 @@ describe('express tracing', () => { }, }, }) - .start(done); + .start(); const body = new Uint8Array([1, 2, 3, 4, 5]).buffer; @@ -236,6 +242,7 @@ describe('express tracing', () => { headers: { 'Content-Type': 'application/octet-stream' }, data: body, }); + await runner.completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/express/tracing/tracesSampler/test.ts b/dev-packages/node-integration-tests/suites/express/tracing/tracesSampler/test.ts index 07cc8d094d8f..1b644ada387a 100644 --- a/dev-packages/node-integration-tests/suites/express/tracing/tracesSampler/test.ts +++ b/dev-packages/node-integration-tests/suites/express/tracing/tracesSampler/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('express tracesSampler', () => { @@ -6,19 +7,20 @@ describe('express tracesSampler', () => { }); describe('CJS', () => { - test('correctly samples & passes data to tracesSampler', done => { + test('correctly samples & passes data to tracesSampler', async () => { const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { transaction: 'GET /test/:id', }, }) - .start(done); + .start(); // This is not sampled runner.makeRequest('get', '/test2?q=1'); // This is sampled runner.makeRequest('get', '/test/123?q=1'); + await runner.completed(); }); }); }); @@ -29,16 +31,17 @@ describe('express tracesSampler includes normalizedRequest data', () => { }); describe('CJS', () => { - test('correctly samples & passes data to tracesSampler', done => { + test('correctly samples & passes data to tracesSampler', async () => { const runner = createRunner(__dirname, 'scenario-normalizedRequest.js') .expect({ transaction: { transaction: 'GET /test-normalized-request', }, }) - .start(done); + .start(); runner.makeRequest('get', '/test-normalized-request?query=123'); + await runner.completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/express/tracing/updateName/test.ts b/dev-packages/node-integration-tests/suites/express/tracing/updateName/test.ts index c6345713fd7e..227cf6042c44 100644 --- a/dev-packages/node-integration-tests/suites/express/tracing/updateName/test.ts +++ b/dev-packages/node-integration-tests/suites/express/tracing/updateName/test.ts @@ -1,5 +1,6 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME } from '@sentry/core'; import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/node'; +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('express tracing', () => { @@ -11,8 +12,8 @@ describe('express tracing', () => { // This test documents the unfortunate behaviour of using `span.updateName` on the server-side. // For http.server root spans (which is the root span on the server 99% of the time), Otel's http instrumentation // calls `span.updateName` and overwrites whatever the name was set to before (by us or by users). - test("calling just `span.updateName` doesn't update the final name in express (missing source)", done => { - createRunner(__dirname, 'server.js') + test("calling just `span.updateName` doesn't update the final name in express (missing source)", async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { transaction: 'GET /test/:id/span-updateName', @@ -21,14 +22,15 @@ describe('express tracing', () => { }, }, }) - .start(done) - .makeRequest('get', '/test/123/span-updateName'); + .start(); + runner.makeRequest('get', '/test/123/span-updateName'); + await runner.completed(); }); // Also calling `updateName` AND setting a source doesn't change anything - Otel has no concept of source, this is sentry-internal. // Therefore, only the source is updated but the name is still overwritten by Otel. - test("calling `span.updateName` and setting attribute source doesn't update the final name in express but it updates the source", done => { - createRunner(__dirname, 'server.js') + test("calling `span.updateName` and setting attribute source doesn't update the final name in express but it updates the source", async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { transaction: 'GET /test/:id/span-updateName-source', @@ -37,13 +39,14 @@ describe('express tracing', () => { }, }, }) - .start(done) - .makeRequest('get', '/test/123/span-updateName-source'); + .start(); + runner.makeRequest('get', '/test/123/span-updateName-source'); + await runner.completed(); }); // This test documents the correct way to update the span name (and implicitly the source) in Node: - test('calling `Sentry.updateSpanName` updates the final name and source in express', done => { - createRunner(__dirname, 'server.js') + test('calling `Sentry.updateSpanName` updates the final name and source in express', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: txnEvent => { expect(txnEvent).toMatchObject({ @@ -62,14 +65,15 @@ describe('express tracing', () => { expect(txnEvent.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toBeUndefined(); }, }) - .start(done) - .makeRequest('get', '/test/123/updateSpanName'); + .start(); + runner.makeRequest('get', '/test/123/updateSpanName'); + await runner.completed(); }); }); // This test documents the correct way to update the span name (and implicitly the source) in Node: - test('calling `Sentry.updateSpanName` and setting source subsequently updates the final name and sets correct source', done => { - createRunner(__dirname, 'server.js') + test('calling `Sentry.updateSpanName` and setting source subsequently updates the final name and sets correct source', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: txnEvent => { expect(txnEvent).toMatchObject({ @@ -88,7 +92,8 @@ describe('express tracing', () => { expect(txnEvent.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_CUSTOM_SPAN_NAME]).toBeUndefined(); }, }) - .start(done) - .makeRequest('get', '/test/123/updateSpanNameAndSource'); + .start(); + runner.makeRequest('get', '/test/123/updateSpanNameAndSource'); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/express/tracing/withError/test.ts b/dev-packages/node-integration-tests/suites/express/tracing/withError/test.ts index 4dd004ad2239..e99c5bf44700 100644 --- a/dev-packages/node-integration-tests/suites/express/tracing/withError/test.ts +++ b/dev-packages/node-integration-tests/suites/express/tracing/withError/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('express tracing experimental', () => { @@ -6,8 +7,8 @@ describe('express tracing experimental', () => { }); describe('CJS', () => { - test('should apply the scope transactionName to error events', done => { - createRunner(__dirname, 'server.js') + test('should apply the scope transactionName to error events', async () => { + const runner = createRunner(__dirname, 'server.js') .ignore('transaction') .expect({ event: { @@ -21,8 +22,9 @@ describe('express tracing experimental', () => { transaction: 'GET /test/:id1/:id2', }, }) - .start(done) - .makeRequest('get', '/test/123/abc?q=1'); + .start(); + runner.makeRequest('get', '/test/123/abc?q=1'); + await runner.completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/express/without-tracing/test.ts b/dev-packages/node-integration-tests/suites/express/without-tracing/test.ts index fdd63ad4aa4b..5dc6aedecbd4 100644 --- a/dev-packages/node-integration-tests/suites/express/without-tracing/test.ts +++ b/dev-packages/node-integration-tests/suites/express/without-tracing/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { @@ -5,7 +6,7 @@ afterAll(() => { }); describe('express without tracing', () => { - test('correctly applies isolation scope even without tracing', done => { + test('correctly applies isolation scope even without tracing', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -25,13 +26,14 @@ describe('express without tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('get', '/test/isolationScope/1'); + await runner.completed(); }); describe('request data', () => { - test('correctly captures JSON request data', done => { + test('correctly captures JSON request data', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -50,12 +52,13 @@ describe('express without tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { data: { foo: 'bar', other: 1 } }); + await runner.completed(); }); - test('correctly captures plain text request data', done => { + test('correctly captures plain text request data', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -71,7 +74,7 @@ describe('express without tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { headers: { @@ -79,9 +82,10 @@ describe('express without tracing', () => { }, data: 'some plain text', }); + await runner.completed(); }); - test('correctly captures text buffer request data', done => { + test('correctly captures text buffer request data', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -97,15 +101,16 @@ describe('express without tracing', () => { }, }, }) - .start(done); + .start(); runner.makeRequest('post', '/test-post', { headers: { 'Content-Type': 'application/octet-stream' }, data: Buffer.from('some plain text in buffer'), }); + await runner.completed(); }); - test('correctly captures non-text buffer request data', done => { + test('correctly captures non-text buffer request data', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ event: { @@ -122,11 +127,12 @@ describe('express without tracing', () => { }, }, }) - .start(done); + .start(); const body = new Uint8Array([1, 2, 3, 4, 5]).buffer; runner.makeRequest('post', '/test-post', { headers: { 'Content-Type': 'application/octet-stream' }, data: body }); + await runner.completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/fs-instrumentation/test.ts b/dev-packages/node-integration-tests/suites/fs-instrumentation/test.ts index 05860fa1ce26..721c569231a0 100644 --- a/dev-packages/node-integration-tests/suites/fs-instrumentation/test.ts +++ b/dev-packages/node-integration-tests/suites/fs-instrumentation/test.ts @@ -1,11 +1,12 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/node'; +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should create spans for fs operations that take target argument', done => { +test('should create spans for fs operations that take target argument', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ transaction: { @@ -25,12 +26,14 @@ test('should create spans for fs operations that take target argument', done => ]), }, }) - .start(done); + .start(); - expect(runner.makeRequest('get', '/readFile-error')).resolves.toBe('done'); + const result = await runner.makeRequest('get', '/readFile-error'); + expect(result).toEqual('done'); + await runner.completed(); }); -test('should create spans for fs operations that take one path', done => { +test('should create spans for fs operations that take one path', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ transaction: { @@ -69,12 +72,14 @@ test('should create spans for fs operations that take one path', done => { ]), }, }) - .start(done); + .start(); - expect(runner.makeRequest('get', '/readFile')).resolves.toBe('done'); + const result = await runner.makeRequest('get', '/readFile'); + expect(result).toEqual('done'); + await runner.completed(); }); -test('should create spans for fs operations that take src and dest arguments', done => { +test('should create spans for fs operations that take src and dest arguments', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ transaction: { @@ -116,12 +121,14 @@ test('should create spans for fs operations that take src and dest arguments', d ]), }, }) - .start(done); + .start(); - expect(runner.makeRequest('get', '/copyFile')).resolves.toBe('done'); + const result = await runner.makeRequest('get', '/copyFile'); + expect(result).toEqual('done'); + await runner.completed(); }); -test('should create spans for fs operations that take existing path and new path arguments', done => { +test('should create spans for fs operations that take existing path and new path arguments', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ transaction: { @@ -163,12 +170,14 @@ test('should create spans for fs operations that take existing path and new path ]), }, }) - .start(done); + .start(); - expect(runner.makeRequest('get', '/link')).resolves.toBe('done'); + const result = await runner.makeRequest('get', '/link'); + expect(result).toEqual('done'); + await runner.completed(); }); -test('should create spans for fs operations that take prefix argument', done => { +test('should create spans for fs operations that take prefix argument', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ transaction: { @@ -207,12 +216,14 @@ test('should create spans for fs operations that take prefix argument', done => ]), }, }) - .start(done); + .start(); - expect(runner.makeRequest('get', '/mkdtemp')).resolves.toBe('done'); + const result = await runner.makeRequest('get', '/mkdtemp'); + expect(result).toEqual('done'); + await runner.completed(); }); -test('should create spans for fs operations that take target argument', done => { +test('should create spans for fs operations that take target argument', async () => { const runner = createRunner(__dirname, 'server.ts') .expect({ transaction: { @@ -254,7 +265,9 @@ test('should create spans for fs operations that take target argument', done => ]), }, }) - .start(done); + .start(); - expect(runner.makeRequest('get', '/symlink')).resolves.toBe('done'); + const result = await runner.makeRequest('get', '/symlink'); + expect(result).toEqual('done'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/no-code/test.ts b/dev-packages/node-integration-tests/suites/no-code/test.ts index fdcd5bd25fc6..dfe58ff03f72 100644 --- a/dev-packages/node-integration-tests/suites/no-code/test.ts +++ b/dev-packages/node-integration-tests/suites/no-code/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../utils/runner'; const EVENT = { @@ -16,21 +17,23 @@ describe('no-code init', () => { cleanupChildProcesses(); }); - test('CJS', done => { - createRunner(__dirname, 'app.js') + test('CJS', async () => { + await createRunner(__dirname, 'app.js') .withFlags('--require=@sentry/node/init') .withMockSentryServer() .expect({ event: EVENT }) - .start(done); + .start() + .completed(); }); describe('--import', () => { - test('ESM', done => { - createRunner(__dirname, 'app.mjs') + test('ESM', async () => { + await createRunner(__dirname, 'app.mjs') .withFlags('--import=@sentry/node/init') .withMockSentryServer() .expect({ event: EVENT }) - .start(done); + .start() + .completed(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/proxy/test.ts b/dev-packages/node-integration-tests/suites/proxy/test.ts index cc2fc0b83404..805b913d4814 100644 --- a/dev-packages/node-integration-tests/suites/proxy/test.ts +++ b/dev-packages/node-integration-tests/suites/proxy/test.ts @@ -1,16 +1,18 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('proxies sentry requests', done => { - createRunner(__dirname, 'basic.js') +test('proxies sentry requests', async () => { + await createRunner(__dirname, 'basic.js') .withMockSentryServer() .expect({ event: { message: 'Hello, via proxy!', }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/LocalVariables/test.ts b/dev-packages/node-integration-tests/suites/public-api/LocalVariables/test.ts index 7ed9d352474a..e95e5a9e3767 100644 --- a/dev-packages/node-integration-tests/suites/public-api/LocalVariables/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/LocalVariables/test.ts @@ -1,10 +1,11 @@ import * as path from 'path'; +import { afterAll, describe, expect, test } from 'vitest'; import { conditionalTest } from '../../../utils'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; // This test takes some time because it connects the debugger etc. // So we increase the timeout here -jest.setTimeout(45_000); +// vi.setTimeout(45_000); const EXPECTED_LOCAL_VARIABLES_EVENT = { exception: { @@ -42,8 +43,8 @@ describe('LocalVariables integration', () => { cleanupChildProcesses(); }); - test('Should not include local variables by default', done => { - createRunner(__dirname, 'no-local-variables.js') + test('Should not include local variables by default', async () => { + await createRunner(__dirname, 'no-local-variables.js') .expect({ event: event => { for (const frame of event.exception?.values?.[0]?.stacktrace?.frames || []) { @@ -51,41 +52,53 @@ describe('LocalVariables integration', () => { } }, }) - .start(done); + .start() + .completed(); }); - test('Should include local variables when enabled', done => { - createRunner(__dirname, 'local-variables.js').expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT }).start(done); + test('Should include local variables when enabled', async () => { + await createRunner(__dirname, 'local-variables.js') + .expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT }) + .start() + .completed(); }); - test('Should include local variables when instrumenting via --require', done => { + test('Should include local variables when instrumenting via --require', async () => { const requirePath = path.resolve(__dirname, 'local-variables-instrument.js'); - createRunner(__dirname, 'local-variables-no-sentry.js') + await createRunner(__dirname, 'local-variables-no-sentry.js') .withFlags(`--require=${requirePath}`) .expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT }) - .start(done); + .start() + .completed(); }); - test('Should include local variables with ESM', done => { - createRunner(__dirname, 'local-variables-caught.mjs').expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT }).start(done); + test('Should include local variables with ESM', async () => { + await createRunner(__dirname, 'local-variables-caught.mjs') + .expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT }) + .start() + .completed(); }); conditionalTest({ min: 19 })('Node v19+', () => { - test('Should not import inspector when not in use', done => { - createRunner(__dirname, 'deny-inspector.mjs').ensureNoErrorOutput().start(done); + test('Should not import inspector when not in use', async () => { + await createRunner(__dirname, 'deny-inspector.mjs').ensureNoErrorOutput().start().completed(); }); }); conditionalTest({ min: 20 })('Node v20+', () => { - test('Should retain original local variables when error is re-thrown', done => { - createRunner(__dirname, 'local-variables-rethrow.js') + test('Should retain original local variables when error is re-thrown', async () => { + await createRunner(__dirname, 'local-variables-rethrow.js') .expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT }) - .start(done); + .start() + .completed(); }); }); - test('Includes local variables for caught exceptions when enabled', done => { - createRunner(__dirname, 'local-variables-caught.js').expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT }).start(done); + test('Includes local variables for caught exceptions when enabled', async () => { + await createRunner(__dirname, 'local-variables-caught.js') + .expect({ event: EXPECTED_LOCAL_VARIABLES_EVENT }) + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/test.ts b/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/test.ts index 60523dc37684..d27b08f152be 100644 --- a/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/OnUncaughtException/test.ts @@ -1,52 +1,13 @@ import * as childProcess from 'child_process'; import * as path from 'path'; +import { describe, expect, test } from 'vitest'; describe('OnUncaughtException integration', () => { - test('should close process on uncaught error with no additional listeners registered', done => { - expect.assertions(3); - - const testScriptPath = path.resolve(__dirname, 'no-additional-listener-test-script.js'); - - childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (err, stdout) => { - expect(err).not.toBeNull(); - expect(err?.code).toBe(1); - expect(stdout).not.toBe("I'm alive!"); - done(); - }); - }); - - test('should not close process on uncaught error when additional listeners are registered', done => { - expect.assertions(2); - - const testScriptPath = path.resolve(__dirname, 'additional-listener-test-script.js'); - - childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (err, stdout) => { - expect(err).toBeNull(); - expect(stdout).toBe("I'm alive!"); - done(); - }); - }); - - test('should log entire error object to console stderr', done => { - expect.assertions(2); - - const testScriptPath = path.resolve(__dirname, 'log-entire-error-to-console.js'); - - childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (err, stderr) => { - expect(err).not.toBeNull(); - const errString = err?.toString() || ''; - - expect(errString).toContain(stderr); - - done(); - }); - }); - - describe('with `exitEvenIfOtherHandlersAreRegistered` set to false', () => { - test('should close process on uncaught error with no additional listeners registered', done => { + test('should close process on uncaught error with no additional listeners registered', () => + new Promise(done => { expect.assertions(3); - const testScriptPath = path.resolve(__dirname, 'mimic-native-behaviour-no-additional-listener-test-script.js'); + const testScriptPath = path.resolve(__dirname, 'no-additional-listener-test-script.js'); childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (err, stdout) => { expect(err).not.toBeNull(); @@ -54,18 +15,63 @@ describe('OnUncaughtException integration', () => { expect(stdout).not.toBe("I'm alive!"); done(); }); - }); + })); - test('should not close process on uncaught error when additional listeners are registered', done => { + test('should not close process on uncaught error when additional listeners are registered', () => + new Promise(done => { expect.assertions(2); - const testScriptPath = path.resolve(__dirname, 'mimic-native-behaviour-additional-listener-test-script.js'); + const testScriptPath = path.resolve(__dirname, 'additional-listener-test-script.js'); childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (err, stdout) => { expect(err).toBeNull(); expect(stdout).toBe("I'm alive!"); done(); }); - }); + })); + + test('should log entire error object to console stderr', () => + new Promise(done => { + expect.assertions(2); + + const testScriptPath = path.resolve(__dirname, 'log-entire-error-to-console.js'); + + childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (err, stderr) => { + expect(err).not.toBeNull(); + const errString = err?.toString() || ''; + + expect(errString).toContain(stderr); + + done(); + }); + })); + + describe('with `exitEvenIfOtherHandlersAreRegistered` set to false', () => { + test('should close process on uncaught error with no additional listeners registered', () => + new Promise(done => { + expect.assertions(3); + + const testScriptPath = path.resolve(__dirname, 'mimic-native-behaviour-no-additional-listener-test-script.js'); + + childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (err, stdout) => { + expect(err).not.toBeNull(); + expect(err?.code).toBe(1); + expect(stdout).not.toBe("I'm alive!"); + done(); + }); + })); + + test('should not close process on uncaught error when additional listeners are registered', () => + new Promise(done => { + expect.assertions(2); + + const testScriptPath = path.resolve(__dirname, 'mimic-native-behaviour-additional-listener-test-script.js'); + + childProcess.exec(`node ${testScriptPath}`, { encoding: 'utf8' }, (err, stdout) => { + expect(err).toBeNull(); + expect(stdout).toBe("I'm alive!"); + done(); + }); + })); }); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts b/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts index 224c0dc5cd7d..c4f5145a8bbf 100644 --- a/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/empty-obj/test.ts @@ -1,15 +1,17 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should add an empty breadcrumb, when an empty object is given', done => { - createRunner(__dirname, 'scenario.ts') +test('should add an empty breadcrumb, when an empty object is given', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'test-empty-obj', }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts b/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts index 9ee47204c635..13dba000a823 100644 --- a/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/multiple_breadcrumbs/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should add multiple breadcrumbs', done => { - createRunner(__dirname, 'scenario.ts') +test('should add multiple breadcrumbs', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'test_multi_breadcrumbs', @@ -21,5 +22,6 @@ test('should add multiple breadcrumbs', done => { ], }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts b/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts index 58b2c56f42fd..9708e00201ae 100644 --- a/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/addBreadcrumb/simple_breadcrumb/test.ts @@ -1,7 +1,8 @@ +import { test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; -test('should add a simple breadcrumb', done => { - createRunner(__dirname, 'scenario.ts') +test('should add a simple breadcrumb', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'test_simple', @@ -14,5 +15,6 @@ test('should add a simple breadcrumb', done => { ], }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts b/dev-packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts index 1ea1bf3b8569..f78c53d5da8c 100644 --- a/dev-packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/captureException/catched-error/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should work inside catch block', done => { - createRunner(__dirname, 'scenario.ts') +test('should work inside catch block', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { exception: { @@ -39,5 +40,6 @@ test('should work inside catch block', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts b/dev-packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts index 4efab7398cb6..b8a6fe4f85e2 100644 --- a/dev-packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/captureException/empty-obj/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should capture an empty object', done => { - createRunner(__dirname, 'scenario.ts') +test('should capture an empty object', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { exception: { @@ -22,5 +23,6 @@ test('should capture an empty object', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts b/dev-packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts index 647edb7c4a13..3afe450398e3 100644 --- a/dev-packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/captureException/simple-error/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should capture a simple error with message', done => { - createRunner(__dirname, 'scenario.ts') +test('should capture a simple error with message', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { exception: { @@ -25,5 +26,6 @@ test('should capture a simple error with message', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/captureMessage/parameterized_message/test.ts b/dev-packages/node-integration-tests/suites/public-api/captureMessage/parameterized_message/test.ts index c67dbc7bb9ce..15e6e76306fe 100644 --- a/dev-packages/node-integration-tests/suites/public-api/captureMessage/parameterized_message/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/captureMessage/parameterized_message/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should capture a parameterized representation of the message', done => { - createRunner(__dirname, 'scenario.ts') +test('should capture a parameterized representation of the message', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { logentry: { @@ -14,5 +15,6 @@ test('should capture a parameterized representation of the message', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts b/dev-packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts index 6e53e6eb7279..e32081747f28 100644 --- a/dev-packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/captureMessage/simple_message/test.ts @@ -1,16 +1,18 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should capture a simple message string', done => { - createRunner(__dirname, 'scenario.ts') +test('should capture a simple message string', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'Message', level: 'info', }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/captureMessage/simple_message_attachStackTrace/test.ts b/dev-packages/node-integration-tests/suites/public-api/captureMessage/simple_message_attachStackTrace/test.ts index 85b29fbcc239..8c79687b2bc4 100644 --- a/dev-packages/node-integration-tests/suites/public-api/captureMessage/simple_message_attachStackTrace/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/captureMessage/simple_message_attachStackTrace/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('capture a simple message string with a stack trace if `attachStackTrace` is `true`', done => { - createRunner(__dirname, 'scenario.ts') +test('capture a simple message string with a stack trace if `attachStackTrace` is `true`', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'Message', @@ -21,5 +22,6 @@ test('capture a simple message string with a stack trace if `attachStackTrace` i }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts b/dev-packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts index a0f16b2f7b78..a44af6931d1f 100644 --- a/dev-packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/captureMessage/with_level/test.ts @@ -1,16 +1,18 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should capture with different severity levels', done => { - createRunner(__dirname, 'scenario.ts') +test('should capture with different severity levels', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'debug_message', level: 'debug' } }) .expect({ event: { message: 'info_message', level: 'info' } }) .expect({ event: { message: 'warning_message', level: 'warning' } }) .expect({ event: { message: 'error_message', level: 'error' } }) .expect({ event: { message: 'fatal_message', level: 'fatal' } }) .expect({ event: { message: 'log_message', level: 'log' } }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts b/dev-packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts index 234a5fed56ad..19f16417bb50 100644 --- a/dev-packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/configureScope/clear_scope/test.ts @@ -1,15 +1,17 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should clear previously set properties of a scope', done => { - createRunner(__dirname, 'scenario.ts') +test('should clear previously set properties of a scope', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'cleared_scope', }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts b/dev-packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts index bb07ce4190d9..ecfb83c3a4a3 100644 --- a/dev-packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/configureScope/set_properties/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should set different properties of a scope', done => { - createRunner(__dirname, 'scenario.ts') +test('should set different properties of a scope', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'configured_scope', @@ -20,5 +21,6 @@ test('should set different properties of a scope', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/scopes/initialScopes/test.ts b/dev-packages/node-integration-tests/suites/public-api/scopes/initialScopes/test.ts index d9b7bc8b2fc9..8f16958cc1c9 100644 --- a/dev-packages/node-integration-tests/suites/public-api/scopes/initialScopes/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/scopes/initialScopes/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should apply scopes correctly', done => { - createRunner(__dirname, 'scenario.ts') +test('should apply scopes correctly', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'outer_before', @@ -34,5 +35,6 @@ test('should apply scopes correctly', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/scopes/isolationScope/test.ts b/dev-packages/node-integration-tests/suites/public-api/scopes/isolationScope/test.ts index 85722a870c09..eb926423ef58 100644 --- a/dev-packages/node-integration-tests/suites/public-api/scopes/isolationScope/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/scopes/isolationScope/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should apply scopes correctly', done => { - createRunner(__dirname, 'scenario.ts') +test('should apply scopes correctly', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'outer_before', @@ -51,5 +52,6 @@ test('should apply scopes correctly', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts b/dev-packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts index 4ede2800470c..1cf8342e2f29 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setContext/multiple-contexts/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should record multiple contexts', done => { - createRunner(__dirname, 'scenario.ts') +test('should record multiple contexts', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'multiple_contexts', @@ -18,5 +19,6 @@ test('should record multiple contexts', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts b/dev-packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts index 17c265b00922..34c962e5e216 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setContext/non-serializable-context/test.ts @@ -1,11 +1,13 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should normalize non-serializable context', done => { - createRunner(__dirname, 'scenario.ts') +test('should normalize non-serializable context', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'non_serializable', contexts: {} } }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts b/dev-packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts index a67efb9148bc..3c28a109130b 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setContext/simple-context/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should set a simple context', done => { - createRunner(__dirname, 'scenario.ts') +test('should set a simple context', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'simple_context_object', @@ -16,5 +17,6 @@ test('should set a simple context', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts b/dev-packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts index feb55d69f8a4..f40d56af6579 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setExtra/multiple-extras/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should record multiple extras of different types', done => { - createRunner(__dirname, 'scenario.ts') +test('should record multiple extras of different types', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'multiple_extras', @@ -15,5 +16,6 @@ test('should record multiple extras of different types', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts b/dev-packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts index f785f44f2d38..113c99883f32 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setExtra/non-serializable-extra/test.ts @@ -1,16 +1,18 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should normalize non-serializable extra', done => { - createRunner(__dirname, 'scenario.ts') +test('should normalize non-serializable extra', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'non_serializable', extra: {}, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts b/dev-packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts index 3180910e5a10..115d4ca064a4 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setExtra/simple-extra/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should set a simple extra', done => { - createRunner(__dirname, 'scenario.ts') +test('should set a simple extra', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'simple_extra', @@ -19,5 +20,6 @@ test('should set a simple extra', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts b/dev-packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts index c6e3cd04c475..da5dc31e9fea 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setExtras/consecutive-calls/test.ts @@ -1,16 +1,18 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should set extras from multiple consecutive calls', done => { - createRunner(__dirname, 'scenario.ts') +test('should set extras from multiple consecutive calls', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'consecutive_calls', extra: { extra: [], Infinity: 2, null: 0, obj: { foo: ['bar', 'baz', 1] } }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts b/dev-packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts index c6282f3f25b1..614a157fed14 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setExtras/multiple-extras/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should record an extras object', done => { - createRunner(__dirname, 'scenario.ts') +test('should record an extras object', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'multiple_extras', @@ -17,5 +18,6 @@ test('should record an extras object', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setMeasurement/test.ts b/dev-packages/node-integration-tests/suites/public-api/setMeasurement/test.ts index 63a32d270a72..829e6a7ed3da 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setMeasurement/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setMeasurement/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should attach measurement to transaction', done => { - createRunner(__dirname, 'scenario.ts') +test('should attach measurement to transaction', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ transaction: { transaction: 'some_transaction', @@ -16,5 +17,6 @@ test('should attach measurement to transaction', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts b/dev-packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts index aaa81e79c705..23e22402c666 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setTag/with-primitives/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should set primitive tags', done => { - createRunner(__dirname, 'scenario.ts') +test('should set primitive tags', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'primitive_tags', @@ -18,5 +19,6 @@ test('should set primitive tags', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts b/dev-packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts index aaa81e79c705..23e22402c666 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setTags/with-primitives/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should set primitive tags', done => { - createRunner(__dirname, 'scenario.ts') +test('should set primitive tags', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'primitive_tags', @@ -18,5 +19,6 @@ test('should set primitive tags', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts b/dev-packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts index a8c5f4483da3..9b7f3e2c23be 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setUser/unset_user/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should unset user', done => { - createRunner(__dirname, 'scenario.ts') +test('should unset user', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'no_user' } }) .expect({ event: { @@ -18,5 +19,6 @@ test('should unset user', done => { }, }) .expect({ event: { message: 'unset_user' } }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts b/dev-packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts index 8f24c3d93949..7a6c89f4c213 100644 --- a/dev-packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/setUser/update_user/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should update user', done => { - createRunner(__dirname, 'scenario.ts') +test('should update user', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'first_user', @@ -23,5 +24,6 @@ test('should update user', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage/test.ts b/dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage/test.ts index 0370b123cab2..ea02312afd43 100644 --- a/dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/startSpan/basic-usage/test.ts @@ -1,12 +1,13 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/node'; +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('sends a manually started root span with source custom', done => { - createRunner(__dirname, 'scenario.ts') +test('sends a manually started root span with source custom', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ transaction: { transaction: 'test_span', @@ -20,11 +21,12 @@ test('sends a manually started root span with source custom', done => { }, }, }) - .start(done); + .start() + .completed(); }); -test("doesn't change the name for manually started spans even if attributes triggering inference are set", done => { - createRunner(__dirname, 'scenario.ts') +test("doesn't change the name for manually started spans even if attributes triggering inference are set", async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ transaction: { transaction: 'test_span', @@ -38,5 +40,6 @@ test("doesn't change the name for manually started spans even if attributes trig }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-root-spans/test.ts b/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-root-spans/test.ts index ed7726d72389..e1b8f793d9b6 100644 --- a/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-root-spans/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-root-spans/test.ts @@ -1,13 +1,14 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should send manually started parallel root spans in root context', done => { +test('should send manually started parallel root spans in root context', async () => { expect.assertions(7); - createRunner(__dirname, 'scenario.ts') + await createRunner(__dirname, 'scenario.ts') .expect({ transaction: { transaction: 'test_span_1' } }) .expect({ transaction: transaction => { @@ -25,5 +26,6 @@ test('should send manually started parallel root spans in root context', done => expect(trace1Id).not.toBe(traceId); }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-spans-in-scope-with-parentSpanId/test.ts b/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-spans-in-scope-with-parentSpanId/test.ts index ecc45e46b4a0..e10a1210a0c9 100644 --- a/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-spans-in-scope-with-parentSpanId/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-spans-in-scope-with-parentSpanId/test.ts @@ -1,11 +1,12 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should send manually started parallel root spans outside of root context with parentSpanId', done => { - createRunner(__dirname, 'scenario.ts') +test('should send manually started parallel root spans outside of root context with parentSpanId', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ transaction: { transaction: 'test_span_1' } }) .expect({ transaction: transaction => { @@ -21,5 +22,6 @@ test('should send manually started parallel root spans outside of root context w expect(trace1Id).not.toBe(traceId); }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-spans-in-scope/test.ts b/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-spans-in-scope/test.ts index 58cf67c7c69a..69fc2bc2774a 100644 --- a/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-spans-in-scope/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/startSpan/parallel-spans-in-scope/test.ts @@ -1,13 +1,14 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should send manually started parallel root spans outside of root context', done => { +test('should send manually started parallel root spans outside of root context', async () => { expect.assertions(6); - createRunner(__dirname, 'scenario.ts') + await createRunner(__dirname, 'scenario.ts') .expect({ transaction: { transaction: 'test_span_1' } }) .expect({ transaction: transaction => { @@ -23,5 +24,6 @@ test('should send manually started parallel root spans outside of root context', expect(trace1Id).not.toBe(traceId); }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/startSpan/updateName-method/test.ts b/dev-packages/node-integration-tests/suites/public-api/startSpan/updateName-method/test.ts index 676071926b91..c46efa9a7fc3 100644 --- a/dev-packages/node-integration-tests/suites/public-api/startSpan/updateName-method/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/startSpan/updateName-method/test.ts @@ -1,11 +1,12 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/node'; +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('updates the span name when calling `span.updateName`', done => { +test('updates the span name when calling `span.updateName`', async () => { createRunner(__dirname, 'scenario.ts') .expect({ transaction: { @@ -20,5 +21,6 @@ test('updates the span name when calling `span.updateName`', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/startSpan/updateSpanName-function/test.ts b/dev-packages/node-integration-tests/suites/public-api/startSpan/updateSpanName-function/test.ts index c5b325fc0ea2..49149d0ed7ec 100644 --- a/dev-packages/node-integration-tests/suites/public-api/startSpan/updateSpanName-function/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/startSpan/updateSpanName-function/test.ts @@ -1,12 +1,13 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '@sentry/node'; +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('updates the span name and source when calling `updateSpanName`', done => { - createRunner(__dirname, 'scenario.ts') +test('updates the span name and source when calling `updateSpanName`', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ transaction: { transaction: 'new name', @@ -20,5 +21,6 @@ test('updates the span name and source when calling `updateSpanName`', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/startSpan/with-nested-spans/test.ts b/dev-packages/node-integration-tests/suites/public-api/startSpan/with-nested-spans/test.ts index 63706d2bc9bc..c01b837db5f7 100644 --- a/dev-packages/node-integration-tests/suites/public-api/startSpan/with-nested-spans/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/startSpan/with-nested-spans/test.ts @@ -1,4 +1,5 @@ import type { SpanJSON } from '@sentry/core'; +import { afterAll, expect, test } from 'vitest'; import { assertSentryTransaction } from '../../../../utils/assertions'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; @@ -6,8 +7,8 @@ afterAll(() => { cleanupChildProcesses(); }); -test('should report finished spans as children of the root transaction.', done => { - createRunner(__dirname, 'scenario.ts') +test('should report finished spans as children of the root transaction.', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ transaction: transaction => { const rootSpanId = transaction.contexts?.trace?.span_id; @@ -41,5 +42,6 @@ test('should report finished spans as children of the root transaction.', done = }); }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts b/dev-packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts index fa7384c319b8..4e646a233443 100644 --- a/dev-packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts +++ b/dev-packages/node-integration-tests/suites/public-api/withScope/nested-scopes/test.ts @@ -1,11 +1,12 @@ +import { afterAll, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; afterAll(() => { cleanupChildProcesses(); }); -test('should allow nested scoping', done => { - createRunner(__dirname, 'scenario.ts') +test('should allow nested scoping', async () => { + await createRunner(__dirname, 'scenario.ts') .expect({ event: { message: 'root_before', @@ -53,5 +54,6 @@ test('should allow nested scoping', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts b/dev-packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts index 0500c702189a..712eeffcdeb3 100644 --- a/dev-packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts +++ b/dev-packages/node-integration-tests/suites/sessions/crashed-session-aggregate/test.ts @@ -1,10 +1,11 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -afterEach(() => { +afterAll(() => { cleanupChildProcesses(); }); -test('should aggregate successful and crashed sessions', done => { +test('should aggregate successful and crashed sessions', async () => { const runner = createRunner(__dirname, '..', 'server.ts') .ignore('transaction', 'event') .unignore('sessions') @@ -19,9 +20,10 @@ test('should aggregate successful and crashed sessions', done => { ], }, }) - .start(done); + .start(); runner.makeRequest('get', '/test/success'); runner.makeRequest('get', '/test/error_unhandled', { expectError: true }); runner.makeRequest('get', '/test/success_next'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts b/dev-packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts index 1159d092fdd7..4f35e6259697 100644 --- a/dev-packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts +++ b/dev-packages/node-integration-tests/suites/sessions/errored-session-aggregate/test.ts @@ -1,10 +1,11 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -afterEach(() => { +afterAll(() => { cleanupChildProcesses(); }); -test('should aggregate successful, crashed and erroneous sessions', done => { +test('should aggregate successful, crashed and erroneous sessions', async () => { const runner = createRunner(__dirname, '..', 'server.ts') .ignore('transaction', 'event') .unignore('sessions') @@ -20,9 +21,10 @@ test('should aggregate successful, crashed and erroneous sessions', done => { ], }, }) - .start(done); + .start(); runner.makeRequest('get', '/test/success'); runner.makeRequest('get', '/test/error_handled'); runner.makeRequest('get', '/test/error_unhandled', { expectError: true }); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/sessions/exited-session-aggregate/test.ts b/dev-packages/node-integration-tests/suites/sessions/exited-session-aggregate/test.ts index 465761e76224..228ee9a98643 100644 --- a/dev-packages/node-integration-tests/suites/sessions/exited-session-aggregate/test.ts +++ b/dev-packages/node-integration-tests/suites/sessions/exited-session-aggregate/test.ts @@ -1,10 +1,11 @@ +import { afterAll, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -afterEach(() => { +afterAll(() => { cleanupChildProcesses(); }); -test('should aggregate successful sessions', done => { +test('should aggregate successful sessions', async () => { const runner = createRunner(__dirname, '..', 'server.ts') .ignore('transaction', 'event') .unignore('sessions') @@ -18,9 +19,10 @@ test('should aggregate successful sessions', done => { ], }, }) - .start(done); + .start(); runner.makeRequest('get', '/test/success'); runner.makeRequest('get', '/test/success_next'); runner.makeRequest('get', '/test/success_slow'); + await runner.completed(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/ai/test.ts b/dev-packages/node-integration-tests/suites/tracing/ai/test.ts index bc263e9fc610..6263d2d26a05 100644 --- a/dev-packages/node-integration-tests/suites/tracing/ai/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/ai/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; // `ai` SDK only support Node 18+ @@ -6,7 +7,7 @@ describe('ai', () => { cleanupChildProcesses(); }); - test('creates ai related spans', done => { + test('creates ai related spans', async () => { const EXPECTED_TRANSACTION = { transaction: 'main', spans: expect.arrayContaining([ @@ -125,6 +126,6 @@ describe('ai', () => { ]), }; - createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done); + await createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml b/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml index dc68d428c976..c1127f097dbf 100644 --- a/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml +++ b/dev-packages/node-integration-tests/suites/tracing/amqplib/docker-compose.yml @@ -8,8 +8,8 @@ services: - RABBITMQ_DEFAULT_USER=sentry - RABBITMQ_DEFAULT_PASS=sentry ports: - - "5672:5672" - - "15672:15672" + - '5672:5672' + - '15672:15672' networks: default: diff --git a/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts b/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts index 7a3eb6414237..250e1e31c40c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/amqplib/test.ts @@ -1,9 +1,7 @@ import type { TransactionEvent } from '@sentry/core'; +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -// When running docker compose, we need a larger timeout, as this takes some time. -jest.setTimeout(90_000); - const EXPECTED_MESSAGE_SPAN_PRODUCER = expect.objectContaining({ op: 'message', data: expect.objectContaining({ @@ -31,8 +29,8 @@ describe('amqplib auto-instrumentation', () => { cleanupChildProcesses(); }); - test('should be able to send and receive messages', done => { - createRunner(__dirname, 'scenario-message.ts') + test('should be able to send and receive messages', { timeout: 90_000 }, async () => { + await createRunner(__dirname, 'scenario-message.ts') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['Time to start RabbitMQ'], @@ -50,6 +48,7 @@ describe('amqplib auto-instrumentation', () => { expect(transaction.contexts?.trace).toMatchObject(EXPECTED_MESSAGE_SPAN_CONSUMER); }, }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts b/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts index 46e05acf940e..c9289efbde8e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../utils/runner'; // Graphql Instrumentation emits some spans by default on server start @@ -6,7 +7,7 @@ const EXPECTED_START_SERVER_TRANSACTION = { }; describe('GraphQL/Apollo Tests', () => { - test('should instrument GraphQL queries used from Apollo Server.', done => { + test('should instrument GraphQL queries used from Apollo Server.', async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -23,13 +24,14 @@ describe('GraphQL/Apollo Tests', () => { ]), }; - createRunner(__dirname, 'scenario-query.js') + await createRunner(__dirname, 'scenario-query.js') .expect({ transaction: EXPECTED_START_SERVER_TRANSACTION }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); - test('should instrument GraphQL mutations used from Apollo Server.', done => { + test('should instrument GraphQL mutations used from Apollo Server.', async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -47,9 +49,10 @@ describe('GraphQL/Apollo Tests', () => { ]), }; - createRunner(__dirname, 'scenario-mutation.js') + await createRunner(__dirname, 'scenario-mutation.js') .expect({ transaction: EXPECTED_START_SERVER_TRANSACTION }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/useOperationNameForRootSpan/test.ts b/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/useOperationNameForRootSpan/test.ts index 234cc4009b38..4aa7616cc73c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/useOperationNameForRootSpan/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/apollo-graphql/useOperationNameForRootSpan/test.ts @@ -1,4 +1,5 @@ import { createRunner } from '../../../../utils/runner'; +import { describe, test, expect } from 'vitest' // Graphql Instrumentation emits some spans by default on server start const EXPECTED_START_SERVER_TRANSACTION = { @@ -6,7 +7,7 @@ const EXPECTED_START_SERVER_TRANSACTION = { }; describe('GraphQL/Apollo Tests > useOperationNameForRootSpan', () => { - test('useOperationNameForRootSpan works with single query operation', done => { + test('useOperationNameForRootSpan works with single query operation', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /test-graphql (query GetHello)', spans: expect.arrayContaining([ @@ -24,13 +25,14 @@ describe('GraphQL/Apollo Tests > useOperationNameForRootSpan', () => { ]), }; - createRunner(__dirname, 'scenario-query.js') + await createRunner(__dirname, 'scenario-query.js') .expect({ transaction: EXPECTED_START_SERVER_TRANSACTION }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); - test('useOperationNameForRootSpan works with single mutation operation', done => { + test('useOperationNameForRootSpan works with single mutation operation', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /test-graphql (mutation TestMutation)', spans: expect.arrayContaining([ @@ -50,13 +52,14 @@ describe('GraphQL/Apollo Tests > useOperationNameForRootSpan', () => { ]), }; - createRunner(__dirname, 'scenario-mutation.js') + await createRunner(__dirname, 'scenario-mutation.js') .expect({ transaction: EXPECTED_START_SERVER_TRANSACTION }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); - test('useOperationNameForRootSpan ignores an invalid root span', done => { + test('useOperationNameForRootSpan ignores an invalid root span', async () => { const EXPECTED_TRANSACTION = { transaction: 'test span name', spans: expect.arrayContaining([ @@ -74,13 +77,14 @@ describe('GraphQL/Apollo Tests > useOperationNameForRootSpan', () => { ]), }; - createRunner(__dirname, 'scenario-invalid-root-span.js') + await createRunner(__dirname, 'scenario-invalid-root-span.js') .expect({ transaction: EXPECTED_START_SERVER_TRANSACTION }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); - test('useOperationNameForRootSpan works with single query operation without name', done => { + test('useOperationNameForRootSpan works with single query operation without name', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /test-graphql (query)', spans: expect.arrayContaining([ @@ -97,13 +101,14 @@ describe('GraphQL/Apollo Tests > useOperationNameForRootSpan', () => { ]), }; - createRunner(__dirname, 'scenario-no-operation-name.js') + await createRunner(__dirname, 'scenario-no-operation-name.js') .expect({ transaction: EXPECTED_START_SERVER_TRANSACTION }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); - test('useOperationNameForRootSpan works with multiple query operations', done => { + test('useOperationNameForRootSpan works with multiple query operations', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /test-graphql (query GetHello, query GetWorld)', spans: expect.arrayContaining([ @@ -132,21 +137,23 @@ describe('GraphQL/Apollo Tests > useOperationNameForRootSpan', () => { ]), }; - createRunner(__dirname, 'scenario-multiple-operations.js') + await createRunner(__dirname, 'scenario-multiple-operations.js') .expect({ transaction: EXPECTED_START_SERVER_TRANSACTION }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); - test('useOperationNameForRootSpan works with more than 5 query operations', done => { + test('useOperationNameForRootSpan works with more than 5 query operations', async () => { const EXPECTED_TRANSACTION = { transaction: 'GET /test-graphql (query GetHello1, query GetHello2, query GetHello3, query GetHello4, query GetHello5, +4)', }; - createRunner(__dirname, 'scenario-multiple-operations-many.js') + await createRunner(__dirname, 'scenario-multiple-operations-many.js') .expect({ transaction: EXPECTED_START_SERVER_TRANSACTION }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts index a416656f6355..8b03de6e6a37 100644 --- a/dev-packages/node-integration-tests/suites/tracing/connect/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/connect/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('connect auto-instrumentation', () => { @@ -35,26 +36,27 @@ describe('connect auto-instrumentation', () => { }, }; - test('CJS - should auto-instrument `connect` package.', done => { - createRunner(__dirname, 'scenario.js') - .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done) - .makeRequest('get', '/'); + test('CJS - should auto-instrument `connect` package.', async () => { + const runner = createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(); + runner.makeRequest('get', '/'); + await runner.completed(); }); - test('CJS - should capture errors in `connect` middleware.', done => { - createRunner(__dirname, 'scenario.js') + test('CJS - should capture errors in `connect` middleware.', async () => { + const runner = createRunner(__dirname, 'scenario.js') .ignore('transaction') .expect({ event: EXPECTED_EVENT }) - .start(done) - .makeRequest('get', '/error'); + .start(); + runner.makeRequest('get', '/error'); + await runner.completed(); }); - test('CJS - should report errored transactions.', done => { - createRunner(__dirname, 'scenario.js') + test('CJS - should report errored transactions.', async () => { + const runner = createRunner(__dirname, 'scenario.js') .ignore('event') .expect({ transaction: { transaction: 'GET /error' } }) - .start(done) - .makeRequest('get', '/error'); + .start(); + runner.makeRequest('get', '/error'); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/dataloader/test.ts b/dev-packages/node-integration-tests/suites/tracing/dataloader/test.ts index 27a2511f1a6e..bd495ea24a7a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/dataloader/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/dataloader/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('dataloader auto-instrumentation', () => { @@ -31,10 +32,9 @@ describe('dataloader auto-instrumentation', () => { ]), }; - test('should auto-instrument `dataloader` package.', done => { - createRunner(__dirname, 'scenario.js') - .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done) - .makeRequest('get', '/'); + test('should auto-instrument `dataloader` package.', async () => { + const runner = createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(); + runner.makeRequest('get', '/'); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/dsc-txn-name-update/test.ts b/dev-packages/node-integration-tests/suites/tracing/dsc-txn-name-update/test.ts index f669f50f5d7b..f450d2150e31 100644 --- a/dev-packages/node-integration-tests/suites/tracing/dsc-txn-name-update/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/dsc-txn-name-update/test.ts @@ -1,12 +1,13 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../utils/runner'; import { createTestServer } from '../../../utils/server'; -test('adds current transaction name to baggage when the txn name is high-quality', done => { +test('adds current transaction name to baggage when the txn name is high-quality', async () => { expect.assertions(5); let traceId: string | undefined; - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { const baggageItems = getBaggageHeaderItems(headers); traceId = baggageItems.find(item => item.startsWith('sentry-trace_id='))?.split('=')[1] as string; @@ -47,21 +48,22 @@ test('adds current transaction name to baggage when the txn name is high-quality 'sentry-transaction=updated-name-2', ]); }) + .start(); + + await createRunner(__dirname, 'scenario-headers.ts') + .withEnv({ SERVER_URL }) + .expect({ + transaction: {}, + }) .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario-headers.ts') - .withEnv({ SERVER_URL }) - .expect({ - transaction: {}, - }) - .start(closeTestServer); - }); + .completed(); + closeTestServer(); }); -test('adds current transaction name to trace envelope header when the txn name is high-quality', done => { +test('adds current transaction name to trace envelope header when the txn name is high-quality', async () => { expect.assertions(4); - createRunner(__dirname, 'scenario-events.ts') + await createRunner(__dirname, 'scenario-events.ts') .expectHeader({ event: { trace: { @@ -117,7 +119,8 @@ test('adds current transaction name to trace envelope header when the txn name i }, }, }) - .start(done); + .start() + .completed(); }); function getBaggageHeaderItems(headers: Record) { diff --git a/dev-packages/node-integration-tests/suites/tracing/envelope-header/error-active-span-unsampled/test.ts b/dev-packages/node-integration-tests/suites/tracing/envelope-header/error-active-span-unsampled/test.ts index 105722a43239..bba04c788282 100644 --- a/dev-packages/node-integration-tests/suites/tracing/envelope-header/error-active-span-unsampled/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/envelope-header/error-active-span-unsampled/test.ts @@ -1,7 +1,8 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; -test('envelope header for error event during active unsampled span is correct', done => { - createRunner(__dirname, 'scenario.ts') +test('envelope header for error event during active unsampled span is correct', async () => { + await createRunner(__dirname, 'scenario.ts') .ignore('transaction') .expectHeader({ event: { @@ -16,5 +17,6 @@ test('envelope header for error event during active unsampled span is correct', }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/envelope-header/error-active-span/test.ts b/dev-packages/node-integration-tests/suites/tracing/envelope-header/error-active-span/test.ts index 51d62deb75af..f11defc490c8 100644 --- a/dev-packages/node-integration-tests/suites/tracing/envelope-header/error-active-span/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/envelope-header/error-active-span/test.ts @@ -1,7 +1,8 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; -test('envelope header for error event during active span is correct', done => { - createRunner(__dirname, 'scenario.ts') +test('envelope header for error event during active span is correct', async () => { + await createRunner(__dirname, 'scenario.ts') .ignore('transaction') .expectHeader({ event: { @@ -17,5 +18,6 @@ test('envelope header for error event during active span is correct', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/envelope-header/error/test.ts b/dev-packages/node-integration-tests/suites/tracing/envelope-header/error/test.ts index efb1ce409efd..9d39209d456f 100644 --- a/dev-packages/node-integration-tests/suites/tracing/envelope-header/error/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/envelope-header/error/test.ts @@ -1,7 +1,8 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; -test('envelope header for error events is correct', done => { - createRunner(__dirname, 'scenario.ts') +test('envelope header for error events is correct', async () => { + await createRunner(__dirname, 'scenario.ts') .expectHeader({ event: { trace: { @@ -12,5 +13,6 @@ test('envelope header for error events is correct', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/envelope-header/sampleRate-propagation/test.ts b/dev-packages/node-integration-tests/suites/tracing/envelope-header/sampleRate-propagation/test.ts index 55223beff4f6..63db6ff4e820 100644 --- a/dev-packages/node-integration-tests/suites/tracing/envelope-header/sampleRate-propagation/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/envelope-header/sampleRate-propagation/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('tracesSampleRate propagation', () => { @@ -7,8 +8,8 @@ describe('tracesSampleRate propagation', () => { const traceId = '12345678123456781234567812345678'; - test('uses sample rate from incoming baggage header in trace envelope item', done => { - createRunner(__dirname, 'server.js') + test('uses sample rate from incoming baggage header in trace envelope item', async () => { + const runner = createRunner(__dirname, 'server.js') .expectHeader({ transaction: { trace: { @@ -20,12 +21,13 @@ describe('tracesSampleRate propagation', () => { }, }, }) - .start(done) - .makeRequest('get', '/test', { - headers: { - 'sentry-trace': `${traceId}-1234567812345678-1`, - baggage: `sentry-sample_rate=0.05,sentry-trace_id=${traceId},sentry-sampled=true,sentry-transaction=myTransaction,sentry-sample_rand=0.42`, - }, - }); + .start(); + runner.makeRequest('get', '/test', { + headers: { + 'sentry-trace': `${traceId}-1234567812345678-1`, + baggage: `sentry-sample_rate=0.05,sentry-trace_id=${traceId},sentry-sampled=true,sentry-transaction=myTransaction,sentry-sample_rand=0.42`, + }, + }); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction-route/test.ts b/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction-route/test.ts index 15088157994d..f4bb6e2b4293 100644 --- a/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction-route/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction-route/test.ts @@ -1,7 +1,8 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; -test('envelope header for transaction event of route correct', done => { - createRunner(__dirname, 'scenario.ts') +test('envelope header for transaction event of route correct', async () => { + await createRunner(__dirname, 'scenario.ts') .expectHeader({ transaction: { trace: { @@ -16,5 +17,6 @@ test('envelope header for transaction event of route correct', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction-url/test.ts b/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction-url/test.ts index 8b5eb84392c9..c4ed5ae4983f 100644 --- a/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction-url/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction-url/test.ts @@ -1,7 +1,8 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; -test('envelope header for transaction event with source=url correct', done => { - createRunner(__dirname, 'scenario.ts') +test('envelope header for transaction event with source=url correct', async () => { + await createRunner(__dirname, 'scenario.ts') .expectHeader({ transaction: { trace: { @@ -15,5 +16,6 @@ test('envelope header for transaction event with source=url correct', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction/test.ts b/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction/test.ts index 1f26a45ffcac..104761d52c86 100644 --- a/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/envelope-header/transaction/test.ts @@ -1,7 +1,8 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; -test('envelope header for transaction event is correct', done => { - createRunner(__dirname, 'scenario.ts') +test('envelope header for transaction event is correct', async () => { + await createRunner(__dirname, 'scenario.ts') .expectHeader({ transaction: { trace: { @@ -16,5 +17,6 @@ test('envelope header for transaction event is correct', done => { }, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts b/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts index 114d3a909188..42f2a3603f95 100644 --- a/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/genericPool/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('genericPool auto instrumentation', () => { @@ -5,7 +6,7 @@ describe('genericPool auto instrumentation', () => { cleanupChildProcesses(); }); - test('should auto-instrument `genericPool` package when calling pool.require()', done => { + test('should auto-instrument `genericPool` package when calling pool.require()', async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -29,6 +30,6 @@ describe('genericPool auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done); + await createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/hapi/test.ts b/dev-packages/node-integration-tests/suites/tracing/hapi/test.ts index 4bd995777248..e783c77e2fc5 100644 --- a/dev-packages/node-integration-tests/suites/tracing/hapi/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/hapi/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('hapi auto-instrumentation', () => { @@ -35,27 +36,27 @@ describe('hapi auto-instrumentation', () => { }, }; - test('CJS - should auto-instrument `@hapi/hapi` package.', done => { - createRunner(__dirname, 'scenario.js') - .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done) - .makeRequest('get', '/'); + test('CJS - should auto-instrument `@hapi/hapi` package.', async () => { + const runner = createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(); + runner.makeRequest('get', '/'); + await runner.completed(); }); - test('CJS - should handle returned plain errors in routes.', done => { - createRunner(__dirname, 'scenario.js') + test('CJS - should handle returned plain errors in routes.', async () => { + const runner = createRunner(__dirname, 'scenario.js') .expect({ transaction: { transaction: 'GET /error', }, }) .expect({ event: EXPECTED_ERROR_EVENT }) - .start(done) - .makeRequest('get', '/error', { expectError: true }); + .start(); + runner.makeRequest('get', '/error', { expectError: true }); + await runner.completed(); }); - test('CJS - should assign parameterized transactionName to error.', done => { - createRunner(__dirname, 'scenario.js') + test('CJS - should assign parameterized transactionName to error.', async () => { + const runner = createRunner(__dirname, 'scenario.js') .expect({ event: { ...EXPECTED_ERROR_EVENT, @@ -63,31 +64,34 @@ describe('hapi auto-instrumentation', () => { }, }) .ignore('transaction') - .start(done) - .makeRequest('get', '/error/123', { expectError: true }); + .start(); + runner.makeRequest('get', '/error/123', { expectError: true }); + await runner.completed(); }); - test('CJS - should handle returned Boom errors in routes.', done => { - createRunner(__dirname, 'scenario.js') + test('CJS - should handle returned Boom errors in routes.', async () => { + const runner = createRunner(__dirname, 'scenario.js') .expect({ transaction: { transaction: 'GET /boom-error', }, }) .expect({ event: EXPECTED_ERROR_EVENT }) - .start(done) - .makeRequest('get', '/boom-error', { expectError: true }); + .start(); + runner.makeRequest('get', '/boom-error', { expectError: true }); + await runner.completed(); }); - test('CJS - should handle promise rejections in routes.', done => { - createRunner(__dirname, 'scenario.js') + test('CJS - should handle promise rejections in routes.', async () => { + const runner = createRunner(__dirname, 'scenario.js') .expect({ transaction: { transaction: 'GET /promise-error', }, }) .expect({ event: EXPECTED_ERROR_EVENT }) - .start(done) - .makeRequest('get', '/promise-error', { expectError: true }); + .start(); + runner.makeRequest('get', '/promise-error', { expectError: true }); + await runner.completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/http-client-spans/fetch-basic/test.ts b/dev-packages/node-integration-tests/suites/tracing/http-client-spans/fetch-basic/test.ts index 006190864fe6..1b599def6be6 100644 --- a/dev-packages/node-integration-tests/suites/tracing/http-client-spans/fetch-basic/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/http-client-spans/fetch-basic/test.ts @@ -1,10 +1,11 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; -test('captures spans for outgoing fetch requests', done => { +test('captures spans for outgoing fetch requests', async () => { expect.assertions(3); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', () => { // Just ensure we're called expect(true).toBe(true); @@ -17,32 +18,33 @@ test('captures spans for outgoing fetch requests', done => { }, 404, ) + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .expect({ + transaction: { + transaction: 'test_transaction', + spans: expect.arrayContaining([ + expect.objectContaining({ + description: expect.stringMatching(/GET .*\/api\/v0/), + op: 'http.client', + origin: 'auto.http.otel.node_fetch', + status: 'ok', + }), + expect.objectContaining({ + description: expect.stringMatching(/GET .*\/api\/v1/), + op: 'http.client', + origin: 'auto.http.otel.node_fetch', + status: 'not_found', + data: expect.objectContaining({ + 'http.response.status_code': 404, + }), + }), + ]), + }, + }) .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .expect({ - transaction: { - transaction: 'test_transaction', - spans: expect.arrayContaining([ - expect.objectContaining({ - description: expect.stringMatching(/GET .*\/api\/v0/), - op: 'http.client', - origin: 'auto.http.otel.node_fetch', - status: 'ok', - }), - expect.objectContaining({ - description: expect.stringMatching(/GET .*\/api\/v1/), - op: 'http.client', - origin: 'auto.http.otel.node_fetch', - status: 'not_found', - data: expect.objectContaining({ - 'http.response.status_code': 404, - }), - }), - ]), - }, - }) - .start(closeTestServer); - }); + .completed(); + closeTestServer(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/http-client-spans/fetch-strip-query/test.ts b/dev-packages/node-integration-tests/suites/tracing/http-client-spans/fetch-strip-query/test.ts index 12bb11727228..797047080283 100644 --- a/dev-packages/node-integration-tests/suites/tracing/http-client-spans/fetch-strip-query/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/http-client-spans/fetch-strip-query/test.ts @@ -1,53 +1,55 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; -test('strips and handles query params in spans of outgoing fetch requests', done => { +test('strips and handles query params in spans of outgoing fetch requests', async () => { expect.assertions(4); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0/users', () => { // Just ensure we're called expect(true).toBe(true); }) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .expect({ - transaction: txn => { - expect(txn.transaction).toEqual('test_transaction'); - expect(txn.spans).toHaveLength(1); - expect(txn.spans?.[0]).toMatchObject({ - data: { - url: `${SERVER_URL}/api/v0/users`, - 'url.full': `${SERVER_URL}/api/v0/users?id=1`, - 'url.path': '/api/v0/users', - 'url.query': '?id=1', - 'url.scheme': 'http', - 'http.query': 'id=1', - 'http.request.method': 'GET', - 'http.request.method_original': 'GET', - 'http.response.header.content-length': 0, - 'http.response.status_code': 200, - 'network.peer.address': '::1', - 'network.peer.port': expect.any(Number), - 'otel.kind': 'CLIENT', - 'server.port': expect.any(Number), - 'user_agent.original': 'node', - 'sentry.op': 'http.client', - 'sentry.origin': 'auto.http.otel.node_fetch', - 'server.address': 'localhost', - }, - description: `GET ${SERVER_URL}/api/v0/users`, - op: 'http.client', - origin: 'auto.http.otel.node_fetch', - status: 'ok', - parent_span_id: txn.contexts?.trace?.span_id, - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: txn.contexts?.trace?.trace_id, - }); + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .expect({ + transaction: txn => { + expect(txn.transaction).toEqual('test_transaction'); + expect(txn.spans).toHaveLength(1); + expect(txn.spans?.[0]).toMatchObject({ + data: { + url: `${SERVER_URL}/api/v0/users`, + 'url.full': `${SERVER_URL}/api/v0/users?id=1`, + 'url.path': '/api/v0/users', + 'url.query': '?id=1', + 'url.scheme': 'http', + 'http.query': 'id=1', + 'http.request.method': 'GET', + 'http.request.method_original': 'GET', + 'http.response.header.content-length': 0, + 'http.response.status_code': 200, + 'network.peer.address': '::1', + 'network.peer.port': expect.any(Number), + 'otel.kind': 'CLIENT', + 'server.port': expect.any(Number), + 'user_agent.original': 'node', + 'sentry.op': 'http.client', + 'sentry.origin': 'auto.http.otel.node_fetch', + 'server.address': 'localhost', }, - }) - .start(closeTestServer); - }); + description: `GET ${SERVER_URL}/api/v0/users`, + op: 'http.client', + origin: 'auto.http.otel.node_fetch', + status: 'ok', + parent_span_id: txn.contexts?.trace?.span_id, + span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: txn.contexts?.trace?.trace_id, + }); + }, + }) + .start() + .completed(); + closeTestServer(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/http-client-spans/http-basic/test.ts b/dev-packages/node-integration-tests/suites/tracing/http-client-spans/http-basic/test.ts index bb642baf0e1c..bb21f7def8f0 100644 --- a/dev-packages/node-integration-tests/suites/tracing/http-client-spans/http-basic/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/http-client-spans/http-basic/test.ts @@ -1,10 +1,11 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; -test('captures spans for outgoing http requests', done => { +test('captures spans for outgoing http requests', async () => { expect.assertions(3); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', () => { // Just ensure we're called expect(true).toBe(true); @@ -17,32 +18,33 @@ test('captures spans for outgoing http requests', done => { }, 404, ) + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .expect({ + transaction: { + transaction: 'test_transaction', + spans: expect.arrayContaining([ + expect.objectContaining({ + description: expect.stringMatching(/GET .*\/api\/v0/), + op: 'http.client', + origin: 'auto.http.otel.http', + status: 'ok', + }), + expect.objectContaining({ + description: expect.stringMatching(/GET .*\/api\/v1/), + op: 'http.client', + origin: 'auto.http.otel.http', + status: 'not_found', + data: expect.objectContaining({ + 'http.response.status_code': 404, + }), + }), + ]), + }, + }) .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .expect({ - transaction: { - transaction: 'test_transaction', - spans: expect.arrayContaining([ - expect.objectContaining({ - description: expect.stringMatching(/GET .*\/api\/v0/), - op: 'http.client', - origin: 'auto.http.otel.http', - status: 'ok', - }), - expect.objectContaining({ - description: expect.stringMatching(/GET .*\/api\/v1/), - op: 'http.client', - origin: 'auto.http.otel.http', - status: 'not_found', - data: expect.objectContaining({ - 'http.response.status_code': 404, - }), - }), - ]), - }, - }) - .start(closeTestServer); - }); + .completed(); + closeTestServer(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/http-client-spans/http-strip-query/test.ts b/dev-packages/node-integration-tests/suites/tracing/http-client-spans/http-strip-query/test.ts index 37b638635eb9..ebe2eff07013 100644 --- a/dev-packages/node-integration-tests/suites/tracing/http-client-spans/http-strip-query/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/http-client-spans/http-strip-query/test.ts @@ -1,53 +1,56 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; -test('strips and handles query params in spans of outgoing http requests', done => { +test('strips and handles query params in spans of outgoing http requests', async () => { expect.assertions(4); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0/users', () => { // Just ensure we're called expect(true).toBe(true); }) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .expect({ - transaction: txn => { - expect(txn.transaction).toEqual('test_transaction'); - expect(txn.spans).toHaveLength(1); - expect(txn.spans?.[0]).toMatchObject({ - data: { - url: `${SERVER_URL}/api/v0/users`, - 'http.url': `${SERVER_URL}/api/v0/users?id=1`, - 'http.target': '/api/v0/users?id=1', - 'http.flavor': '1.1', - 'http.host': expect.stringMatching(/localhost:\d+$/), - 'http.method': 'GET', - 'http.query': 'id=1', - 'http.response.status_code': 200, - 'http.response_content_length_uncompressed': 0, - 'http.status_code': 200, - 'http.status_text': 'OK', - 'net.peer.ip': '::1', - 'net.peer.name': 'localhost', - 'net.peer.port': expect.any(Number), - 'net.transport': 'ip_tcp', - 'otel.kind': 'CLIENT', - 'sentry.op': 'http.client', - 'sentry.origin': 'auto.http.otel.http', - }, - description: `GET ${SERVER_URL}/api/v0/users`, - op: 'http.client', - origin: 'auto.http.otel.http', - status: 'ok', - parent_span_id: txn.contexts?.trace?.span_id, - span_id: expect.stringMatching(/[a-f0-9]{16}/), - trace_id: txn.contexts?.trace?.trace_id, - }); + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .expect({ + transaction: txn => { + expect(txn.transaction).toEqual('test_transaction'); + expect(txn.spans).toHaveLength(1); + expect(txn.spans?.[0]).toMatchObject({ + data: { + url: `${SERVER_URL}/api/v0/users`, + 'http.url': `${SERVER_URL}/api/v0/users?id=1`, + 'http.target': '/api/v0/users?id=1', + 'http.flavor': '1.1', + 'http.host': expect.stringMatching(/localhost:\d+$/), + 'http.method': 'GET', + 'http.query': 'id=1', + 'http.response.status_code': 200, + 'http.response_content_length_uncompressed': 0, + 'http.status_code': 200, + 'http.status_text': 'OK', + 'net.peer.ip': '::1', + 'net.peer.name': 'localhost', + 'net.peer.port': expect.any(Number), + 'net.transport': 'ip_tcp', + 'otel.kind': 'CLIENT', + 'sentry.op': 'http.client', + 'sentry.origin': 'auto.http.otel.http', }, - }) - .start(closeTestServer); - }); + description: `GET ${SERVER_URL}/api/v0/users`, + op: 'http.client', + origin: 'auto.http.otel.http', + status: 'ok', + parent_span_id: txn.contexts?.trace?.span_id, + span_id: expect.stringMatching(/[a-f0-9]{16}/), + trace_id: txn.contexts?.trace?.trace_id, + }); + }, + }) + .start() + .completed(); + + closeTestServer(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts index 83f36fd7a9ef..6e4ad622ea26 100644 --- a/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; import { createTestServer } from '../../../utils/server'; @@ -6,15 +7,14 @@ describe('httpIntegration', () => { cleanupChildProcesses(); }); - test('allows to pass instrumentation options to integration', done => { + test('allows to pass instrumentation options to integration', async () => { // response shape seems different on Node 14, so we skip this there const nodeMajorVersion = Number(process.versions.node.split('.')[0]); if (nodeMajorVersion <= 14) { - done(); return; } - createRunner(__dirname, 'server.js') + const runner = createRunner(__dirname, 'server.js') .expect({ transaction: { contexts: { @@ -50,12 +50,13 @@ describe('httpIntegration', () => { }, }, }) - .start(done) - .makeRequest('get', '/test'); + .start(); + runner.makeRequest('get', '/test'); + await runner.completed(); }); - test('allows to pass experimental config through to integration', done => { - createRunner(__dirname, 'server-experimental.js') + test('allows to pass experimental config through to integration', async () => { + const runner = createRunner(__dirname, 'server-experimental.js') .expect({ transaction: { contexts: { @@ -73,12 +74,13 @@ describe('httpIntegration', () => { }, }, }) - .start(done) - .makeRequest('get', '/test'); + .start(); + runner.makeRequest('get', '/test'); + await runner.completed(); }); describe("doesn't create a root span for incoming requests ignored via `ignoreIncomingRequests`", () => { - test('via the url param', done => { + test('via the url param', async () => { const runner = createRunner(__dirname, 'server-ignoreIncomingRequests.js') .expect({ transaction: { @@ -97,13 +99,14 @@ describe('httpIntegration', () => { transaction: 'GET /test', }, }) - .start(done); + .start(); runner.makeRequest('get', '/liveness'); // should be ignored runner.makeRequest('get', '/test'); + await runner.completed(); }); - test('via the request param', done => { + test('via the request param', async () => { const runner = createRunner(__dirname, 'server-ignoreIncomingRequests.js') .expect({ transaction: { @@ -122,64 +125,68 @@ describe('httpIntegration', () => { transaction: 'GET /test', }, }) - .start(done); + .start(); runner.makeRequest('post', '/readiness'); // should be ignored runner.makeRequest('get', '/test'); + await runner.completed(); }); }); describe("doesn't create child spans or breadcrumbs for outgoing requests ignored via `ignoreOutgoingRequests`", () => { - test('via the url param', done => { - createTestServer(done) + test('via the url param', async () => { + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/blockUrl', () => {}, 200) .get('/pass', () => {}, 200) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'server-ignoreOutgoingRequests.js') - .withEnv({ SERVER_URL }) - .expect({ - transaction: event => { - expect(event.transaction).toBe('GET /testUrl'); - - const requestSpans = event.spans?.filter(span => span.op === 'http.client'); - expect(requestSpans).toHaveLength(1); - expect(requestSpans![0]?.description).toBe(`GET ${SERVER_URL}/pass`); - - const breadcrumbs = event.breadcrumbs?.filter(b => b.category === 'http'); - expect(breadcrumbs).toHaveLength(1); - expect(breadcrumbs![0]?.data?.url).toEqual(`${SERVER_URL}/pass`); - }, - }) - .start(closeTestServer) - .makeRequest('get', '/testUrl'); - }); + .start(); + + const runner = createRunner(__dirname, 'server-ignoreOutgoingRequests.js') + .withEnv({ SERVER_URL }) + .expect({ + transaction: event => { + expect(event.transaction).toBe('GET /testUrl'); + + const requestSpans = event.spans?.filter(span => span.op === 'http.client'); + expect(requestSpans).toHaveLength(1); + expect(requestSpans![0]?.description).toBe(`GET ${SERVER_URL}/pass`); + + const breadcrumbs = event.breadcrumbs?.filter(b => b.category === 'http'); + expect(breadcrumbs).toHaveLength(1); + expect(breadcrumbs![0]?.data?.url).toEqual(`${SERVER_URL}/pass`); + }, + }) + .start(closeTestServer); + runner.makeRequest('get', '/testUrl'); + await runner.completed(); + closeTestServer(); }); - test('via the request param', done => { - createTestServer(done) + test('via the request param', async () => { + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/blockUrl', () => {}, 200) .get('/pass', () => {}, 200) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'server-ignoreOutgoingRequests.js') - .withEnv({ SERVER_URL }) - .expect({ - transaction: event => { - expect(event.transaction).toBe('GET /testRequest'); - - const requestSpans = event.spans?.filter(span => span.op === 'http.client'); - expect(requestSpans).toHaveLength(1); - expect(requestSpans![0]?.description).toBe(`GET ${SERVER_URL}/pass`); - - const breadcrumbs = event.breadcrumbs?.filter(b => b.category === 'http'); - expect(breadcrumbs).toHaveLength(1); - expect(breadcrumbs![0]?.data?.url).toEqual(`${SERVER_URL}/pass`); - }, - }) - .start(closeTestServer) - .makeRequest('get', '/testRequest'); - }); + .start(); + + const runner = createRunner(__dirname, 'server-ignoreOutgoingRequests.js') + .withEnv({ SERVER_URL }) + .expect({ + transaction: event => { + expect(event.transaction).toBe('GET /testRequest'); + + const requestSpans = event.spans?.filter(span => span.op === 'http.client'); + expect(requestSpans).toHaveLength(1); + expect(requestSpans![0]?.description).toBe(`GET ${SERVER_URL}/pass`); + + const breadcrumbs = event.breadcrumbs?.filter(b => b.category === 'http'); + expect(breadcrumbs).toHaveLength(1); + expect(breadcrumbs![0]?.data?.url).toEqual(`${SERVER_URL}/pass`); + }, + }) + .start(); + runner.makeRequest('get', '/testRequest'); + + await runner.completed(); + closeTestServer(); }); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts b/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts index f818af1d676a..f1d6e46db743 100644 --- a/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/kafkajs/test.ts @@ -1,15 +1,13 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -// When running docker compose, we need a larger timeout, as this takes some time... -jest.setTimeout(60_000); - describe('kafkajs', () => { afterAll(() => { cleanupChildProcesses(); }); - test('traces producers and consumers', done => { - createRunner(__dirname, 'scenario.js') + test('traces producers and consumers', { timeout: 60_000 }, async () => { + await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['9092'], @@ -50,6 +48,7 @@ describe('kafkajs', () => { }, }, }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/knex/test.ts b/dev-packages/node-integration-tests/suites/tracing/knex/test.ts index 3ededda4f162..8fd445d51525 100644 --- a/dev-packages/node-integration-tests/suites/tracing/knex/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/knex/test.ts @@ -1,13 +1,11 @@ +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../utils/runner'; -// When running docker compose, we need a larger timeout, as this takes some time... -jest.setTimeout(90000); - describe('knex auto instrumentation', () => { // Update this if another knex version is installed const KNEX_VERSION = '2.5.1'; - test('should auto-instrument `knex` package when using `pg` client', done => { + test('should auto-instrument `knex` package when using `pg` client', { timeout: 60_000 }, async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -60,13 +58,14 @@ describe('knex auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario-withPostgres.js') + await createRunner(__dirname, 'scenario-withPostgres.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); - test('should auto-instrument `knex` package when using `mysql2` client', done => { + test('should auto-instrument `knex` package when using `mysql2` client', { timeout: 60_000 }, async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -121,9 +120,10 @@ describe('knex auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario-withMysql2.js') + await createRunner(__dirname, 'scenario-withMysql2.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port: 3306'] }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/linking/test.ts b/dev-packages/node-integration-tests/suites/tracing/linking/test.ts index 57f68c1d258f..a0874274d2bd 100644 --- a/dev-packages/node-integration-tests/suites/tracing/linking/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/linking/test.ts @@ -1,10 +1,11 @@ +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../utils/runner'; describe('span links', () => { - test('should link spans by adding "links" to span options', done => { + test('should link spans by adding "links" to span options', async () => { let span1_traceId: string, span1_spanId: string; - createRunner(__dirname, 'scenario-span-options.ts') + await createRunner(__dirname, 'scenario-span-options.ts') .expect({ transaction: event => { expect(event.transaction).toBe('parent1'); @@ -28,13 +29,14 @@ describe('span links', () => { ]); }, }) - .start(done); + .start() + .completed(); }); - test('should link spans with addLink() in trace context', done => { + test('should link spans with addLink() in trace context', async () => { let span1_traceId: string, span1_spanId: string; - createRunner(__dirname, 'scenario-addLink.ts') + await createRunner(__dirname, 'scenario-addLink.ts') .expect({ transaction: event => { expect(event.transaction).toBe('span1'); @@ -60,13 +62,14 @@ describe('span links', () => { ]); }, }) - .start(done); + .start() + .completed(); }); - test('should link spans with addLinks() in trace context', done => { + test('should link spans with addLinks() in trace context', async () => { let span1_traceId: string, span1_spanId: string, span2_traceId: string, span2_spanId: string; - createRunner(__dirname, 'scenario-addLinks.ts') + await createRunner(__dirname, 'scenario-addLinks.ts') .expect({ transaction: event => { expect(event.transaction).toBe('span1'); @@ -107,11 +110,12 @@ describe('span links', () => { ]); }, }) - .start(done); + .start() + .completed(); }); - test('should link spans with addLink() in nested startSpan() calls', done => { - createRunner(__dirname, 'scenario-addLink-nested.ts') + test('should link spans with addLink() in nested startSpan() calls', async () => { + await createRunner(__dirname, 'scenario-addLink-nested.ts') .expect({ transaction: event => { expect(event.transaction).toBe('parent1'); @@ -146,11 +150,12 @@ describe('span links', () => { ]); }, }) - .start(done); + .start() + .completed(); }); - test('should link spans with addLinks() in nested startSpan() calls', done => { - createRunner(__dirname, 'scenario-addLinks-nested.ts') + test('should link spans with addLinks() in nested startSpan() calls', async () => { + await createRunner(__dirname, 'scenario-addLinks-nested.ts') .expect({ transaction: event => { expect(event.transaction).toBe('parent1'); @@ -182,6 +187,7 @@ describe('span links', () => { ]); }, }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/lru-memoizer/test.ts b/dev-packages/node-integration-tests/suites/tracing/lru-memoizer/test.ts index 050505e4055e..7caccfc97144 100644 --- a/dev-packages/node-integration-tests/suites/tracing/lru-memoizer/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/lru-memoizer/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('lru-memoizer', () => { @@ -5,8 +6,8 @@ describe('lru-memoizer', () => { cleanupChildProcesses(); }); - test('keeps outer context inside the memoized inner functions', done => { - createRunner(__dirname, 'scenario.js') + test('keeps outer context inside the memoized inner functions', async () => { + await createRunner(__dirname, 'scenario.js') // We expect only one transaction and nothing else. // A failed test will result in an error event being sent to Sentry. // Which will fail this suite. @@ -24,6 +25,7 @@ describe('lru-memoizer', () => { }, }, }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/maxSpans/test.ts b/dev-packages/node-integration-tests/suites/tracing/maxSpans/test.ts index b705bfc71ea4..31b0af762d9a 100644 --- a/dev-packages/node-integration-tests/suites/tracing/maxSpans/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/maxSpans/test.ts @@ -1,18 +1,20 @@ import type { SpanJSON } from '@sentry/core'; +import { expect, test } from 'vitest'; import { createRunner } from '../../../utils/runner'; -test('it limits spans to 1000', done => { +test('it limits spans to 1000', async () => { const expectedSpans: SpanJSON[] = []; for (let i = 0; i < 1000; i++) { expectedSpans.push(expect.objectContaining({ description: `child ${i}` })); } - createRunner(__dirname, 'scenario.ts') + await createRunner(__dirname, 'scenario.ts') .expect({ transaction: { transaction: 'parent', spans: expectedSpans, }, }) - .start(done); + .start() + .completed(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/meta-tags-twp-errors/test.ts b/dev-packages/node-integration-tests/suites/tracing/meta-tags-twp-errors/test.ts index d4447255bf51..7c6612a0f4f7 100644 --- a/dev-packages/node-integration-tests/suites/tracing/meta-tags-twp-errors/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/meta-tags-twp-errors/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('errors in TwP mode have same trace in trace context and getTraceData()', () => { @@ -6,8 +7,8 @@ describe('errors in TwP mode have same trace in trace context and getTraceData() }); // In a request handler, the spanId is consistent inside of the request - test('in incoming request', done => { - createRunner(__dirname, 'server.js') + test('in incoming request', async () => { + const runner = createRunner(__dirname, 'server.js') .expect({ event: event => { const { contexts } = event; @@ -27,13 +28,14 @@ describe('errors in TwP mode have same trace in trace context and getTraceData() expect(traceData.metaTags).not.toContain('sentry-sampled='); }, }) - .start(done) - .makeRequest('get', '/test'); + .start(); + runner.makeRequest('get', '/test'); + await runner.completed(); }); // Outside of a request handler, the spanId is random - test('outside of a request handler', done => { - createRunner(__dirname, 'no-server.js') + test('outside of a request handler', async () => { + await createRunner(__dirname, 'no-server.js') .expect({ event: event => { const { contexts } = event; @@ -59,6 +61,7 @@ describe('errors in TwP mode have same trace in trace context and getTraceData() expect(traceData.metaTags).not.toContain('sentry-sampled='); }, }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/meta-tags-twp/test.ts b/dev-packages/node-integration-tests/suites/tracing/meta-tags-twp/test.ts index cca1f34321a2..c719a82ceb36 100644 --- a/dev-packages/node-integration-tests/suites/tracing/meta-tags-twp/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/meta-tags-twp/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('getTraceMetaTags', () => { diff --git a/dev-packages/node-integration-tests/suites/tracing/meta-tags/test.ts b/dev-packages/node-integration-tests/suites/tracing/meta-tags/test.ts index 0d61adf18913..bd617f021778 100644 --- a/dev-packages/node-integration-tests/suites/tracing/meta-tags/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/meta-tags/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('getTraceMetaTags', () => { diff --git a/dev-packages/node-integration-tests/suites/tracing/mongodb/test.ts b/dev-packages/node-integration-tests/suites/tracing/mongodb/test.ts index 92fc857ed4e8..60c5d56e1779 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongodb/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mongodb/test.ts @@ -1,5 +1,6 @@ import { MongoMemoryServer } from 'mongodb-memory-server-global'; +import { afterAll, beforeAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('MongoDB experimental Test', () => { @@ -170,7 +171,7 @@ describe('MongoDB experimental Test', () => { ], }; - test('CJS - should auto-instrument `mongodb` package.', done => { - createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done); + test('CJS - should auto-instrument `mongodb` package.', async () => { + await createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/mongoose/test.ts b/dev-packages/node-integration-tests/suites/tracing/mongoose/test.ts index 30eeb62ffe31..ad423354a4c9 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mongoose/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mongoose/test.ts @@ -1,5 +1,6 @@ import { MongoMemoryServer } from 'mongodb-memory-server-global'; +import { afterAll, beforeAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('Mongoose experimental Test', () => { @@ -45,7 +46,7 @@ describe('Mongoose experimental Test', () => { ]), }; - test('CJS - should auto-instrument `mongoose` package.', done => { - createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done); + test('CJS - should auto-instrument `mongoose` package.', async () => { + await createRunner(__dirname, 'scenario.js').expect({ transaction: EXPECTED_TRANSACTION }).start().completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/mysql/test.ts b/dev-packages/node-integration-tests/suites/tracing/mysql/test.ts index 530349ae3fb7..adb35f1c0025 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mysql/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mysql/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('mysql auto instrumentation', () => { @@ -5,7 +6,7 @@ describe('mysql auto instrumentation', () => { cleanupChildProcesses(); }); - test('should auto-instrument `mysql` package when using connection.connect()', done => { + test('should auto-instrument `mysql` package when using connection.connect()', async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -32,10 +33,13 @@ describe('mysql auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario-withConnect.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done); + await createRunner(__dirname, 'scenario-withConnect.js') + .expect({ transaction: EXPECTED_TRANSACTION }) + .start() + .completed(); }); - test('should auto-instrument `mysql` package when using query without callback', done => { + test('should auto-instrument `mysql` package when using query without callback', async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -62,10 +66,13 @@ describe('mysql auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario-withoutCallback.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done); + await createRunner(__dirname, 'scenario-withoutCallback.js') + .expect({ transaction: EXPECTED_TRANSACTION }) + .start() + .completed(); }); - test('should auto-instrument `mysql` package without connection.connect()', done => { + test('should auto-instrument `mysql` package without connection.connect()', async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -92,6 +99,9 @@ describe('mysql auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario-withoutConnect.js').expect({ transaction: EXPECTED_TRANSACTION }).start(done); + await createRunner(__dirname, 'scenario-withoutConnect.js') + .expect({ transaction: EXPECTED_TRANSACTION }) + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts b/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts index 60070308c7db..c1d680b9a52e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/mysql2/test.ts @@ -1,14 +1,12 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -// When running docker compose, we need a larger timeout, as this takes some time... -jest.setTimeout(75000); - describe('mysql2 auto instrumentation', () => { afterAll(() => { cleanupChildProcesses(); }); - test('should auto-instrument `mysql` package without connection.connect()', done => { + test('should auto-instrument `mysql` package without connection.connect()', { timeout: 75_000 }, async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -35,9 +33,10 @@ describe('mysql2 auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario.js') + await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port: 3306'] }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts index f2549c70eb90..c34619e161ad 100644 --- a/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/postgres/test.ts @@ -1,10 +1,8 @@ +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../utils/runner'; -// When running docker compose, we need a larger timeout, as this takes some time... -jest.setTimeout(75000); - describe('postgres auto instrumentation', () => { - test('should auto-instrument `pg` package', done => { + test('should auto-instrument `pg` package', { timeout: 60_000 }, async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -48,9 +46,10 @@ describe('postgres auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario.js') + await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'] }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts index da47ef8bc415..b7788d58e372 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v5/test.ts @@ -1,15 +1,13 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -// When running docker compose, we need a larger timeout, as this takes some time... -jest.setTimeout(75000); - afterAll(() => { cleanupChildProcesses(); }); describe('Prisma ORM v5 Tests', () => { - test('CJS - should instrument PostgreSQL queries from Prisma ORM', done => { - createRunner(__dirname, 'scenario.js') + test('CJS - should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { + await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'], @@ -76,6 +74,7 @@ describe('Prisma ORM v5 Tests', () => { ); }, }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts index 10cb5b15c95c..5e00a00966b7 100644 --- a/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/prisma-orm-v6/test.ts @@ -1,16 +1,14 @@ import type { SpanJSON } from '@sentry/core'; +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -// When running docker compose, we need a larger timeout, as this takes some time... -jest.setTimeout(75000); - afterAll(() => { cleanupChildProcesses(); }); describe('Prisma ORM v6 Tests', () => { - test('CJS - should instrument PostgreSQL queries from Prisma ORM', done => { - createRunner(__dirname, 'scenario.js') + test('CJS - should instrument PostgreSQL queries from Prisma ORM', { timeout: 75_000 }, async () => { + await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port 5432'], @@ -92,6 +90,7 @@ describe('Prisma ORM v6 Tests', () => { }); }, }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts b/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts index 2b93c4d772bc..e1aa0b9c1494 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis-cache/test.ts @@ -1,14 +1,12 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -// When running docker compose, we need a larger timeout, as this takes some time... -jest.setTimeout(90000); - describe('redis cache auto instrumentation', () => { afterAll(() => { cleanupChildProcesses(); }); - test('should not add cache spans when key is not prefixed', done => { + test('should not add cache spans when key is not prefixed', { timeout: 60_000 }, async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Span', spans: expect.arrayContaining([ @@ -39,13 +37,14 @@ describe('redis cache auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario-ioredis.js') + await createRunner(__dirname, 'scenario-ioredis.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); - test('should create cache spans for prefixed keys (ioredis)', done => { + test('should create cache spans for prefixed keys (ioredis)', { timeout: 60_000 }, async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Span', spans: expect.arrayContaining([ @@ -137,13 +136,14 @@ describe('redis cache auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario-ioredis.js') + await createRunner(__dirname, 'scenario-ioredis.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); - test('should create cache spans for prefixed keys (redis-4)', done => { + test('should create cache spans for prefixed keys (redis-4)', async () => { const EXPECTED_REDIS_CONNECT = { transaction: 'redis-connect', }; @@ -227,10 +227,11 @@ describe('redis cache auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario-redis-4.js') + await createRunner(__dirname, 'scenario-redis-4.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) .expect({ transaction: EXPECTED_REDIS_CONNECT }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/redis/test.ts b/dev-packages/node-integration-tests/suites/tracing/redis/test.ts index 5a801a2f6a5b..0c9bb65facdc 100644 --- a/dev-packages/node-integration-tests/suites/tracing/redis/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/redis/test.ts @@ -1,14 +1,12 @@ +import { afterAll, describe, expect, test} from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -// When running docker compose, we need a larger timeout, as this takes some time... -jest.setTimeout(75000); - describe('redis auto instrumentation', () => { afterAll(() => { cleanupChildProcesses(); }); - test('should auto-instrument `ioredis` package when using redis.set() and redis.get()', done => { + test('should auto-instrument `ioredis` package when using redis.set() and redis.get()', { timeout: 75_000 }, async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Span', spans: expect.arrayContaining([ @@ -41,9 +39,10 @@ describe('redis auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario-ioredis.js') + await createRunner(__dirname, 'scenario-ioredis.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['port=6379'] }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/fetch-breadcrumbs/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/fetch-breadcrumbs/test.ts index 254d197c85c3..c14a6ab528ac 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/fetch-breadcrumbs/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/fetch-breadcrumbs/test.ts @@ -1,77 +1,78 @@ +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; describe('outgoing fetch', () => { - test('outgoing fetch requests create breadcrumbs', done => { - createTestServer(done) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .ensureNoErrorOutput() - .expect({ - event: { - breadcrumbs: [ - { - message: 'manual breadcrumb', - timestamp: expect.any(Number), - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v0`, - status_code: 404, - ADDED_PATH: '/api/v0', - }, - timestamp: expect.any(Number), - type: 'http', - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v1`, - status_code: 404, - ADDED_PATH: '/api/v1', - }, - timestamp: expect.any(Number), - type: 'http', - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v2`, - status_code: 404, - ADDED_PATH: '/api/v2', - }, - timestamp: expect.any(Number), - type: 'http', - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v3`, - status_code: 404, - ADDED_PATH: '/api/v3', - }, - timestamp: expect.any(Number), - type: 'http', - }, - ], - exception: { - values: [ - { - type: 'Error', - value: 'foo', - }, - ], + test('outgoing fetch requests create breadcrumbs', async () => { + const [SERVER_URL, closeTestServer] = await createTestServer().start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .ensureNoErrorOutput() + .expect({ + event: { + breadcrumbs: [ + { + message: 'manual breadcrumb', + timestamp: expect.any(Number), + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v0`, + status_code: 404, + ADDED_PATH: '/api/v0', }, + timestamp: expect.any(Number), + type: 'http', }, - }) - .start(closeTestServer); - }); + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v1`, + status_code: 404, + ADDED_PATH: '/api/v1', + }, + timestamp: expect.any(Number), + type: 'http', + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v2`, + status_code: 404, + ADDED_PATH: '/api/v2', + }, + timestamp: expect.any(Number), + type: 'http', + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v3`, + status_code: 404, + ADDED_PATH: '/api/v3', + }, + timestamp: expect.any(Number), + type: 'http', + }, + ], + exception: { + values: [ + { + type: 'Error', + value: 'foo', + }, + ], + }, + }, + }) + .start() + .completed(); + closeTestServer(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/fetch-no-tracing-no-spans/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/fetch-no-tracing-no-spans/test.ts index 98fc6bd38c52..0da3d7fd6501 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/fetch-no-tracing-no-spans/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/fetch-no-tracing-no-spans/test.ts @@ -1,11 +1,12 @@ +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; describe('outgoing fetch', () => { - test('outgoing fetch requests are correctly instrumented with tracing & spans are disabled', done => { + test('outgoing fetch requests are correctly instrumented with tracing & spans are disabled', async () => { expect.assertions(11); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})$/)); expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000'); @@ -24,24 +25,25 @@ describe('outgoing fetch', () => { expect(headers['baggage']).toBeUndefined(); expect(headers['sentry-trace']).toBeUndefined(); }) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .ensureNoErrorOutput() - .expect({ - event: { - exception: { - values: [ - { - type: 'Error', - value: 'foo', - }, - ], + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .ensureNoErrorOutput() + .expect({ + event: { + exception: { + values: [ + { + type: 'Error', + value: 'foo', }, - }, - }) - .start(closeTestServer); - }); + ], + }, + }, + }) + .start() + .completed(); + closeTestServer; }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/fetch-no-tracing/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/fetch-no-tracing/test.ts index 906fa6541dd6..1975199514ff 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/fetch-no-tracing/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/fetch-no-tracing/test.ts @@ -1,11 +1,12 @@ +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; describe('outgoing fetch', () => { - test('outgoing fetch requests are correctly instrumented with tracing disabled', done => { + test('outgoing fetch requests are correctly instrumented with tracing disabled', async () => { expect.assertions(11); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})$/)); expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000'); @@ -24,24 +25,25 @@ describe('outgoing fetch', () => { expect(headers['baggage']).toBeUndefined(); expect(headers['sentry-trace']).toBeUndefined(); }) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .ensureNoErrorOutput() - .expect({ - event: { - exception: { - values: [ - { - type: 'Error', - value: 'foo', - }, - ], + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .ensureNoErrorOutput() + .expect({ + event: { + exception: { + values: [ + { + type: 'Error', + value: 'foo', }, - }, - }) - .start(closeTestServer); - }); + ], + }, + }, + }) + .start() + .completed(); + closeTestServer(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/fetch-sampled-no-active-span/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/fetch-sampled-no-active-span/test.ts index afe60d27b22a..00da2285f060 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/fetch-sampled-no-active-span/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/fetch-sampled-no-active-span/test.ts @@ -1,11 +1,12 @@ +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; describe('outgoing fetch', () => { - test('outgoing sampled fetch requests without active span are correctly instrumented', done => { + test('outgoing sampled fetch requests without active span are correctly instrumented', async () => { expect.assertions(11); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { expect(headers['baggage']).toEqual(expect.any(String)); expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})$/)); @@ -24,23 +25,24 @@ describe('outgoing fetch', () => { expect(headers['baggage']).toBeUndefined(); expect(headers['sentry-trace']).toBeUndefined(); }) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .expect({ - event: { - exception: { - values: [ - { - type: 'Error', - value: 'foo', - }, - ], + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .expect({ + event: { + exception: { + values: [ + { + type: 'Error', + value: 'foo', }, - }, - }) - .start(closeTestServer); - }); + ], + }, + }, + }) + .start() + .completed(); + closeTestServer(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/fetch-unsampled/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/fetch-unsampled/test.ts index cb85ca98ca0b..b6dcf5e6b116 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/fetch-unsampled/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/fetch-unsampled/test.ts @@ -1,11 +1,12 @@ +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; describe('outgoing fetch', () => { - test('outgoing fetch requests are correctly instrumented when not sampled', done => { + test('outgoing fetch requests are correctly instrumented when not sampled', async () => { expect.assertions(11); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { expect(headers['baggage']).toEqual(expect.any(String)); expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-0$/)); @@ -24,23 +25,24 @@ describe('outgoing fetch', () => { expect(headers['baggage']).toBeUndefined(); expect(headers['sentry-trace']).toBeUndefined(); }) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .expect({ - event: { - exception: { - values: [ - { - type: 'Error', - value: 'foo', - }, - ], + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .expect({ + event: { + exception: { + values: [ + { + type: 'Error', + value: 'foo', }, - }, - }) - .start(closeTestServer); - }); + ], + }, + }, + }) + .start() + .completed(); + closeTestServer(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/http-breadcrumbs/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/http-breadcrumbs/test.ts index 812dbbb4ae60..86d61866ad38 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/http-breadcrumbs/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/http-breadcrumbs/test.ts @@ -1,75 +1,76 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; -test('outgoing http requests create breadcrumbs', done => { - createTestServer(done) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .ensureNoErrorOutput() - .expect({ - event: { - breadcrumbs: [ - { - message: 'manual breadcrumb', - timestamp: expect.any(Number), - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v0`, - status_code: 404, - ADDED_PATH: '/api/v0', - }, - timestamp: expect.any(Number), - type: 'http', - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v1`, - status_code: 404, - ADDED_PATH: '/api/v1', - }, - timestamp: expect.any(Number), - type: 'http', - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v2`, - status_code: 404, - ADDED_PATH: '/api/v2', - }, - timestamp: expect.any(Number), - type: 'http', - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v3`, - status_code: 404, - ADDED_PATH: '/api/v3', - }, - timestamp: expect.any(Number), - type: 'http', - }, - ], - exception: { - values: [ - { - type: 'Error', - value: 'foo', - }, - ], +test('outgoing http requests create breadcrumbs', async () => { + const [SERVER_URL, closeTestServer] = await createTestServer().start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .ensureNoErrorOutput() + .expect({ + event: { + breadcrumbs: [ + { + message: 'manual breadcrumb', + timestamp: expect.any(Number), + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v0`, + status_code: 404, + ADDED_PATH: '/api/v0', }, + timestamp: expect.any(Number), + type: 'http', }, - }) - .start(closeTestServer); - }); + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v1`, + status_code: 404, + ADDED_PATH: '/api/v1', + }, + timestamp: expect.any(Number), + type: 'http', + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v2`, + status_code: 404, + ADDED_PATH: '/api/v2', + }, + timestamp: expect.any(Number), + type: 'http', + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v3`, + status_code: 404, + ADDED_PATH: '/api/v3', + }, + timestamp: expect.any(Number), + type: 'http', + }, + ], + exception: { + values: [ + { + type: 'Error', + value: 'foo', + }, + ], + }, + }, + }) + .start() + .completed(); + closeTestServer(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing/test.ts index 3ab1090806cb..77b6f10217ed 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/http-no-tracing/test.ts @@ -1,10 +1,11 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; -test('outgoing http requests are correctly instrumented with tracing disabled', done => { +test('outgoing http requests are correctly instrumented with tracing disabled', async () => { expect.assertions(11); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})$/)); expect(headers['sentry-trace']).not.toEqual('00000000000000000000000000000000-0000000000000000'); @@ -23,73 +24,74 @@ test('outgoing http requests are correctly instrumented with tracing disabled', expect(headers['baggage']).toBeUndefined(); expect(headers['sentry-trace']).toBeUndefined(); }) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .ensureNoErrorOutput() - .expect({ - event: { - exception: { - values: [ - { - type: 'Error', - value: 'foo', - }, - ], + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .ensureNoErrorOutput() + .expect({ + event: { + exception: { + values: [ + { + type: 'Error', + value: 'foo', + }, + ], + }, + breadcrumbs: [ + { + message: 'manual breadcrumb', + timestamp: expect.any(Number), + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v0`, + status_code: 200, + ADDED_PATH: '/api/v0', + }, + timestamp: expect.any(Number), + type: 'http', + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v1`, + status_code: 200, + ADDED_PATH: '/api/v1', }, - breadcrumbs: [ - { - message: 'manual breadcrumb', - timestamp: expect.any(Number), - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v0`, - status_code: 200, - ADDED_PATH: '/api/v0', - }, - timestamp: expect.any(Number), - type: 'http', - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v1`, - status_code: 200, - ADDED_PATH: '/api/v1', - }, - timestamp: expect.any(Number), - type: 'http', - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v2`, - status_code: 200, - ADDED_PATH: '/api/v2', - }, - timestamp: expect.any(Number), - type: 'http', - }, - { - category: 'http', - data: { - 'http.method': 'GET', - url: `${SERVER_URL}/api/v3`, - status_code: 200, - ADDED_PATH: '/api/v3', - }, - timestamp: expect.any(Number), - type: 'http', - }, - ], + timestamp: expect.any(Number), + type: 'http', }, - }) - .start(closeTestServer); - }); + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v2`, + status_code: 200, + ADDED_PATH: '/api/v2', + }, + timestamp: expect.any(Number), + type: 'http', + }, + { + category: 'http', + data: { + 'http.method': 'GET', + url: `${SERVER_URL}/api/v3`, + status_code: 200, + ADDED_PATH: '/api/v3', + }, + timestamp: expect.any(Number), + type: 'http', + }, + ], + }, + }) + .start() + .completed(); + closeTestServer(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled-esm/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled-esm/test.ts index f3d58877c8f3..05c8913a9d2d 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled-esm/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled-esm/test.ts @@ -1,12 +1,13 @@ import { join } from 'path'; +import { describe, expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; describe('outgoing http in ESM', () => { - test('outgoing sampled http requests are correctly instrumented in ESM', done => { + test('outgoing sampled http requests are correctly instrumented in ESM', async () => { expect.assertions(11); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { expect(headers['baggage']).toEqual(expect.any(String)); expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-1$/)); @@ -25,18 +26,19 @@ describe('outgoing http in ESM', () => { expect(headers['baggage']).toBeUndefined(); expect(headers['sentry-trace']).toBeUndefined(); }) + .start(); + + const instrumentPath = join(__dirname, 'instrument.mjs'); + await createRunner(__dirname, 'scenario.mjs') + .withFlags('--import', instrumentPath) + .withEnv({ SERVER_URL }) + .expect({ + transaction: { + // we're not too concerned with the actual transaction here since this is tested elsewhere + }, + }) .start() - .then(([SERVER_URL, closeTestServer]) => { - const instrumentPath = join(__dirname, 'instrument.mjs'); - createRunner(__dirname, 'scenario.mjs') - .withFlags('--import', instrumentPath) - .withEnv({ SERVER_URL }) - .expect({ - transaction: { - // we're not too concerned with the actual transaction here since this is tested elsewhere - }, - }) - .start(closeTestServer); - }); + .completed(); + closeTestServer(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled-no-active-span/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled-no-active-span/test.ts index 2baff11a5faf..6811dc3bb45e 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled-no-active-span/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled-no-active-span/test.ts @@ -1,10 +1,11 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; -test('outgoing sampled http requests without active span are correctly instrumented', done => { +test('outgoing sampled http requests without active span are correctly instrumented', async () => { expect.assertions(11); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { expect(headers['baggage']).toEqual(expect.any(String)); expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})$/)); @@ -23,22 +24,23 @@ test('outgoing sampled http requests without active span are correctly instrumen expect(headers['baggage']).toBeUndefined(); expect(headers['sentry-trace']).toBeUndefined(); }) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .expect({ - event: { - exception: { - values: [ - { - type: 'Error', - value: 'foo', - }, - ], + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .expect({ + event: { + exception: { + values: [ + { + type: 'Error', + value: 'foo', }, - }, - }) - .start(closeTestServer); - }); + ], + }, + }, + }) + .start() + .completed(); + closeTestServer(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled/test.ts index 38dfa6524019..0e52c83af91b 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/http-sampled/test.ts @@ -1,10 +1,11 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; -test('outgoing sampled http requests are correctly instrumented', done => { +test('outgoing sampled http requests are correctly instrumented', async () => { expect.assertions(11); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { expect(headers['baggage']).toEqual(expect.any(String)); expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-1$/)); @@ -23,15 +24,16 @@ test('outgoing sampled http requests are correctly instrumented', done => { expect(headers['baggage']).toBeUndefined(); expect(headers['sentry-trace']).toBeUndefined(); }) + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .expect({ + transaction: { + // we're not too concerned with the actual transaction here since this is tested elsewhere + }, + }) .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .expect({ - transaction: { - // we're not too concerned with the actual transaction here since this is tested elsewhere - }, - }) - .start(closeTestServer); - }); + .completed(); + closeTestServer(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/requests/http-unsampled/test.ts b/dev-packages/node-integration-tests/suites/tracing/requests/http-unsampled/test.ts index 3d2e0e421863..140ef37908f3 100644 --- a/dev-packages/node-integration-tests/suites/tracing/requests/http-unsampled/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/requests/http-unsampled/test.ts @@ -1,10 +1,11 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../../utils/runner'; import { createTestServer } from '../../../../utils/server'; -test('outgoing http requests are correctly instrumented when not sampled', done => { +test('outgoing http requests are correctly instrumented when not sampled', async () => { expect.assertions(11); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { expect(headers['baggage']).toEqual(expect.any(String)); expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-0$/)); @@ -23,22 +24,23 @@ test('outgoing http requests are correctly instrumented when not sampled', done expect(headers['baggage']).toBeUndefined(); expect(headers['sentry-trace']).toBeUndefined(); }) - .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .expect({ - event: { - exception: { - values: [ - { - type: 'Error', - value: 'foo', - }, - ], + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .expect({ + event: { + exception: { + values: [ + { + type: 'Error', + value: 'foo', }, - }, - }) - .start(closeTestServer); - }); + ], + }, + }, + }) + .start() + .completed(); + closeTestServer(); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/sample-rand-propagation/test.ts b/dev-packages/node-integration-tests/suites/tracing/sample-rand-propagation/test.ts index 7c566c1d8eeb..df25a261ab2c 100644 --- a/dev-packages/node-integration-tests/suites/tracing/sample-rand-propagation/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/sample-rand-propagation/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; describe('sample_rand propagation', () => { diff --git a/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/no-tracing-enabled/test.ts b/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/no-tracing-enabled/test.ts index c6800055c84b..b3040dc0cfa4 100644 --- a/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/no-tracing-enabled/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/no-tracing-enabled/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('parentSampleRate propagation with no tracing enabled', () => { diff --git a/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampleRate-0/test.ts b/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampleRate-0/test.ts index c12d2920dd9f..833f04e431a6 100644 --- a/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampleRate-0/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampleRate-0/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('parentSampleRate propagation with tracesSampleRate=0', () => { diff --git a/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampleRate/test.ts b/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampleRate/test.ts index 27afa03e9045..8166c72adcb6 100644 --- a/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampleRate/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampleRate/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('parentSampleRate propagation with tracesSampleRate', () => { diff --git a/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampler/test.ts b/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampler/test.ts index f97773711941..c50e37c87780 100644 --- a/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampler/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/sample-rate-propagation/tracesSampler/test.ts @@ -1,3 +1,4 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../../utils/runner'; describe('parentSampleRate propagation with tracesSampler', () => { diff --git a/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts b/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts index 6c5fd17f833a..797ede718b39 100644 --- a/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/tedious/test.ts @@ -1,15 +1,12 @@ +import { afterAll, describe, expect, test } from 'vitest'; import { cleanupChildProcesses, createRunner } from '../../../utils/runner'; -jest.setTimeout(75000); - -// Tedious version we are testing against only supports Node 18+ -// https://github.com/tediousjs/tedious/blob/8310c455a2cc1cba83c1ca3c16677da4f83e12a9/package.json#L38 -describe('tedious auto instrumentation', () => { +describe('tedious auto instrumentation', {timeout: 75_000}, () => { afterAll(() => { cleanupChildProcesses(); }); - test('should auto-instrument `tedious` package', done => { + test('should auto-instrument `tedious` package', async () => { const EXPECTED_TRANSACTION = { transaction: 'Test Transaction', spans: expect.arrayContaining([ @@ -44,9 +41,10 @@ describe('tedious auto instrumentation', () => { ]), }; - createRunner(__dirname, 'scenario.js') + await createRunner(__dirname, 'scenario.js') .withDockerCompose({ workingDirectory: [__dirname], readyMatches: ['1433'] }) .expect({ transaction: EXPECTED_TRANSACTION }) - .start(done); + .start() + .completed(); }); }); diff --git a/dev-packages/node-integration-tests/suites/tracing/tracePropagationTargets/test.ts b/dev-packages/node-integration-tests/suites/tracing/tracePropagationTargets/test.ts index e6081bedd8ea..4185dec46afb 100644 --- a/dev-packages/node-integration-tests/suites/tracing/tracePropagationTargets/test.ts +++ b/dev-packages/node-integration-tests/suites/tracing/tracePropagationTargets/test.ts @@ -1,10 +1,11 @@ +import { expect, test } from 'vitest'; import { createRunner } from '../../../utils/runner'; import { createTestServer } from '../../../utils/server'; -test('HttpIntegration should instrument correct requests when tracePropagationTargets option is provided', done => { +test('HttpIntegration should instrument correct requests when tracePropagationTargets option is provided', async () => { expect.assertions(11); - createTestServer(done) + const [SERVER_URL, closeTestServer] = await createTestServer() .get('/api/v0', headers => { expect(headers['baggage']).toEqual(expect.any(String)); expect(headers['sentry-trace']).toEqual(expect.stringMatching(/^([a-f0-9]{32})-([a-f0-9]{16})-1$/)); @@ -23,15 +24,16 @@ test('HttpIntegration should instrument correct requests when tracePropagationTa expect(headers['baggage']).toBeUndefined(); expect(headers['sentry-trace']).toBeUndefined(); }) + .start(); + + await createRunner(__dirname, 'scenario.ts') + .withEnv({ SERVER_URL }) + .expect({ + transaction: { + // we're not too concerned with the actual transaction here since this is tested elsewhere + }, + }) .start() - .then(([SERVER_URL, closeTestServer]) => { - createRunner(__dirname, 'scenario.ts') - .withEnv({ SERVER_URL }) - .expect({ - transaction: { - // we're not too concerned with the actual transaction here since this is tested elsewhere - }, - }) - .start(closeTestServer); - }); + .completed(); + closeTestServer(); }); diff --git a/dev-packages/node-integration-tests/tsconfig.test.json b/dev-packages/node-integration-tests/tsconfig.test.json index 3bd3452a3ead..45a6e39b0054 100644 --- a/dev-packages/node-integration-tests/tsconfig.test.json +++ b/dev-packages/node-integration-tests/tsconfig.test.json @@ -1,14 +1,14 @@ { "extends": "./tsconfig.json", - "include": ["suites/**/*.ts"], + "include": ["suites/**/*.ts", "vite.config.ts"], "compilerOptions": { // Although this seems wrong to include `DOM` here, it's necessary to make // global fetch available in tests in lower Node versions. "lib": ["DOM", "ES2018"], // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node"] // other package-specific, test-specific options } diff --git a/dev-packages/node-integration-tests/utils/assertions.ts b/dev-packages/node-integration-tests/utils/assertions.ts index 1d4e7d1b321a..4197c4a70844 100644 --- a/dev-packages/node-integration-tests/utils/assertions.ts +++ b/dev-packages/node-integration-tests/utils/assertions.ts @@ -8,6 +8,7 @@ import type { SessionAggregates, TransactionEvent, } from '@sentry/core'; +import { expect } from 'vitest'; /** * Asserts against a Sentry Event ignoring non-deterministic properties diff --git a/dev-packages/node-integration-tests/utils/index.ts b/dev-packages/node-integration-tests/utils/index.ts index b51a0a8f83c8..bf1086d82a8f 100644 --- a/dev-packages/node-integration-tests/utils/index.ts +++ b/dev-packages/node-integration-tests/utils/index.ts @@ -1,6 +1,7 @@ import type * as http from 'http'; import { parseSemver } from '@sentry/core'; import type { EnvelopeItemType } from '@sentry/core'; +import { describe } from 'vitest'; const NODE_VERSION = parseSemver(process.versions.node).major; @@ -31,9 +32,8 @@ export type DataCollectorOptions = { * Returns`describe` or `describe.skip` depending on allowed major versions of Node. * * @param {{ min?: number; max?: number }} allowedVersion - * @return {*} {jest.Describe} */ -export const conditionalTest = (allowedVersion: { min?: number; max?: number }): jest.Describe => { +export function conditionalTest(allowedVersion: { min?: number; max?: number }): typeof describe | typeof describe.skip{ if (!NODE_VERSION) { return describe.skip; } @@ -41,7 +41,7 @@ export const conditionalTest = (allowedVersion: { min?: number; max?: number }): return NODE_VERSION < (allowedVersion.min || -Infinity) || NODE_VERSION > (allowedVersion.max || Infinity) ? describe.skip : describe; -}; +} /** * Parses response body containing an Envelope diff --git a/dev-packages/node-integration-tests/utils/runner.ts b/dev-packages/node-integration-tests/utils/runner.ts index 152160f7118b..1d77e80bf55c 100644 --- a/dev-packages/node-integration-tests/utils/runner.ts +++ b/dev-packages/node-integration-tests/utils/runner.ts @@ -146,6 +146,17 @@ type ExpectedEnvelopeHeader = | { session: Partial } | { sessions: Partial }; +type StartResult = { + completed(): Promise; + childHasExited(): boolean; + getLogs(): string[]; + makeRequest( + method: 'get' | 'post', + path: string, + options?: { headers?: Record; data?: unknown; expectError?: boolean }, + ): Promise; +}; + /** Creates a test runner */ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type export function createRunner(...paths: string[]) { @@ -219,7 +230,13 @@ export function createRunner(...paths: string[]) { ensureNoErrorOutput = true; return this; }, - start: function (done?: (e?: unknown) => void) { + start: function (done?: (e?: unknown) => void): StartResult { + let resolve: (value: void) => void; + let reject: (reason?: unknown) => void; + const completePromise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); const expectedEnvelopeCount = Math.max(expectedEnvelopes.length, (expectedEnvelopeHeaders || []).length); let envelopeCount = 0; @@ -230,6 +247,11 @@ export function createRunner(...paths: string[]) { function complete(error?: Error): void { child?.kill(); done?.(normalize(error)); + if (error) { + reject(error); + } else { + resolve(); + } } /** Called after each expect callback to check if we're complete */ @@ -254,7 +276,7 @@ export function createRunner(...paths: string[]) { try { if (!expected) { - throw new Error(`No more expected envelope items but we received ${JSON.stringify(header)}`); + return; } assertEnvelopeHeader(header, expected); @@ -272,13 +294,17 @@ export function createRunner(...paths: string[]) { // Catch any error or failed assertions and pass them to done to end the test quickly try { if (!expected) { - throw new Error(`No more expected envelope items but we received a '${envelopeItemType}' item`); + return; } const expectedType = Object.keys(expected)[0]; if (expectedType !== envelopeItemType) { - throw new Error(`Expected envelope item type '${expectedType}' but got '${envelopeItemType}'`); + throw new Error( + `Expected envelope item type '${expectedType}' but got '${envelopeItemType}'. \nItem: ${JSON.stringify( + item, + )}`, + ); } if ('event' in expected) { @@ -300,7 +326,9 @@ export function createRunner(...paths: string[]) { expectClientReport(item[1] as ClientReport, expected.client_report); expectCallbackCalled(); } else { - throw new Error(`Unhandled expected envelope item type: ${JSON.stringify(expected)}`); + throw new Error( + `Unhandled expected envelope item type: ${JSON.stringify(expected)}\nItem: ${JSON.stringify(item)}`, + ); } } catch (e) { complete(e as Error); @@ -415,6 +443,9 @@ export function createRunner(...paths: string[]) { .catch(e => complete(e)); return { + completed: function (): Promise { + return completePromise; + }, childHasExited: function (): boolean { return hasExited; }, diff --git a/dev-packages/node-integration-tests/utils/server.ts b/dev-packages/node-integration-tests/utils/server.ts index 761ec1cd2429..93ba10e425df 100644 --- a/dev-packages/node-integration-tests/utils/server.ts +++ b/dev-packages/node-integration-tests/utils/server.ts @@ -42,8 +42,9 @@ type HeaderAssertCallback = (headers: Record void) { +export function createTestServer() { const gets: Array<[string, HeaderAssertCallback, number]> = []; + let error: unknown | undefined; return { get: function (path: string, callback: HeaderAssertCallback, result = 200) { @@ -58,7 +59,7 @@ export function createTestServer(done: (error?: unknown) => void) { try { callback(req.headers); } catch (e) { - done(e); + error = e; } res.status(result).send(); @@ -70,9 +71,11 @@ export function createTestServer(done: (error?: unknown) => void) { const address = server.address() as AddressInfo; resolve([ `http://localhost:${address.port}`, - (error?: unknown) => { + () => { server.close(); - done(error); + if (error) { + throw error; + } }, ]); }); diff --git a/dev-packages/node-integration-tests/vite.config.ts b/dev-packages/node-integration-tests/vite.config.ts new file mode 100644 index 000000000000..38d4abb0b16a --- /dev/null +++ b/dev-packages/node-integration-tests/vite.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from 'vitest/config'; +import baseConfig from '../../vite/vite.config'; + +export default defineConfig({ + ...baseConfig, + test: { + ...baseConfig.test, + isolate: false, + coverage: { + enabled: false, + }, + include: ['./**/test.ts'], + testTimeout: 15000, + }, +}); diff --git a/dev-packages/rollup-utils/npmHelpers.mjs b/dev-packages/rollup-utils/npmHelpers.mjs index cf9b723599dd..7cd41344e596 100644 --- a/dev-packages/rollup-utils/npmHelpers.mjs +++ b/dev-packages/rollup-utils/npmHelpers.mjs @@ -15,7 +15,6 @@ import { defineConfig } from 'rollup'; import { makeCleanupPlugin, makeDebugBuildStatementReplacePlugin, - makeImportMetaUrlReplacePlugin, makeNodeResolvePlugin, makeRrwebBuildPlugin, makeSucrasePlugin, @@ -39,7 +38,6 @@ export function makeBaseNPMConfig(options = {}) { const nodeResolvePlugin = makeNodeResolvePlugin(); const sucrasePlugin = makeSucrasePlugin({}, sucrase); const debugBuildStatementReplacePlugin = makeDebugBuildStatementReplacePlugin(); - const importMetaUrlReplacePlugin = makeImportMetaUrlReplacePlugin(); const cleanupPlugin = makeCleanupPlugin(); const rrwebBuildPlugin = makeRrwebBuildPlugin({ excludeShadowDom: undefined, @@ -85,14 +83,7 @@ export function makeBaseNPMConfig(options = {}) { interop: 'esModule', }, - plugins: [ - nodeResolvePlugin, - sucrasePlugin, - debugBuildStatementReplacePlugin, - importMetaUrlReplacePlugin, - rrwebBuildPlugin, - cleanupPlugin, - ], + plugins: [nodeResolvePlugin, sucrasePlugin, debugBuildStatementReplacePlugin, rrwebBuildPlugin, cleanupPlugin], // don't include imported modules from outside the package in the final output external: [ diff --git a/dev-packages/rollup-utils/plugins/npmPlugins.mjs b/dev-packages/rollup-utils/plugins/npmPlugins.mjs index f29bded61f73..8c7e0ff10a80 100644 --- a/dev-packages/rollup-utils/plugins/npmPlugins.mjs +++ b/dev-packages/rollup-utils/plugins/npmPlugins.mjs @@ -125,19 +125,6 @@ export function makeDebugBuildStatementReplacePlugin() { }); } -/** - * Because jest doesn't like `import.meta` statements but we still need it in the code base we instead use a magic - * string that we replace with import.meta.url in the build. - */ -export function makeImportMetaUrlReplacePlugin() { - return replace({ - preventAssignment: false, - values: { - __IMPORT_META_URL_REPLACEMENT__: 'import.meta.url', - }, - }); -} - /** * Creates a plugin to replace build flags of rrweb with either a constant (if passed true/false) or with a safe statement that: * a) evaluates to `true` diff --git a/dev-packages/size-limit-gh-action/action.yml b/dev-packages/size-limit-gh-action/action.yml index bfaa23fd85b3..0ac790603889 100644 --- a/dev-packages/size-limit-gh-action/action.yml +++ b/dev-packages/size-limit-gh-action/action.yml @@ -6,12 +6,12 @@ inputs: description: 'a github access token' comparison_branch: required: false - default: "" - description: "If set, compare the current branch with this branch" + default: '' + description: 'If set, compare the current branch with this branch' threshold: required: false - default: "0.0125" - description: "The percentage threshold for size changes before posting a comment" + default: '0.0125' + description: 'The percentage threshold for size changes before posting a comment' runs: using: 'node20' main: 'index.mjs' diff --git a/dev-packages/size-limit-gh-action/index.mjs b/dev-packages/size-limit-gh-action/index.mjs index c12f263f9ea9..8fd5e200fc53 100644 --- a/dev-packages/size-limit-gh-action/index.mjs +++ b/dev-packages/size-limit-gh-action/index.mjs @@ -1,3 +1,4 @@ +/* eslint-disable complexity */ import { promises as fs } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -182,6 +183,24 @@ async function run() { } if (status > 0) { + try { + const results = limit.parseResults(output); + const failedResults = results + .filter(result => result.passed || false) + .map(result => ({ + name: result.name, + size: +result.size, + sizeLimit: +result.sizeLimit, + })); + + if (failedResults.length > 0) { + // eslint-disable-next-line no-console + console.log('Exceeded size-limits:', failedResults); + } + } catch { + // noop + } + setFailed('Size limit has been exceeded.'); } } catch (error) { diff --git a/dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs b/dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs index 034281b38224..d7b3a444ff7c 100644 --- a/dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs +++ b/dev-packages/size-limit-gh-action/utils/SizeLimitFormatter.mjs @@ -91,7 +91,6 @@ export class SizeLimitFormatter { return results.reduce((current, result) => { return { - // biome-ignore lint/performance/noAccumulatingSpread: ...current, [result.name]: { name: result.name, diff --git a/package.json b/package.json index 1f63d4c57dda..460d2c42abb1 100644 --- a/package.json +++ b/package.json @@ -20,17 +20,14 @@ "clean:tarballs": "rimraf {packages,dev-packages}/*/*.tgz", "clean:watchman": "watchman watch-del \".\"", "clean:all": "run-s clean:build clean:tarballs clean:caches clean:deps clean:watchman", - "fix": "run-s fix:biome fix:prettier fix:lerna", + "fix": "run-s fix:prettier fix:lerna", "fix:lerna": "lerna run fix", - "fix:biome": "biome check --apply .", - "fix:prettier": "prettier \"**/*.md\" \"**/*.css\" --write", + "fix:prettier": "prettier \"**/*.{md,css,yml,yaml}\" \"packages/**/**.{ts,js,mjs,cjs,mts,cts,jsx,tsx,astro,vue}\" --write", "changelog": "ts-node ./scripts/get-commit-list.ts", "link:yarn": "lerna exec yarn link", - "lint": "run-s lint:lerna lint:biome lint:prettier", - "lint:clang": "lerna run lint:clang", + "lint": "run-s lint:prettier lint:lerna", "lint:lerna": "lerna run lint", - "lint:biome": "biome check .", - "lint:prettier": "prettier \"**/*.md\" \"**/*.css\" --check", + "lint:prettier": "prettier \"**/*.{md,css,yml,yaml}\" \"packages/**/**.{ts,js,mjs,cjs,mts,cts,jsx,tsx,astro,vue}\" --check", "lint:es-compatibility": "es-check es2020 ./packages/*/build/{bundles,npm/cjs,cjs}/*.js && es-check es2020 ./packages/*/build/{npm/esm,esm}/*.js --module", "postpublish": "lerna run --stream --concurrency 1 postpublish", "test": "lerna run --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests}\" test", @@ -82,6 +79,7 @@ "packages/solidstart", "packages/svelte", "packages/sveltekit", + "packages/tanstackstart", "packages/types", "packages/typescript", "packages/vercel-edge", @@ -98,7 +96,6 @@ "dev-packages/rollup-utils" ], "devDependencies": { - "@biomejs/biome": "^1.5.2", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-esm-shim": "^0.1.5", "@rollup/plugin-json": "^6.1.0", diff --git a/packages/angular/src/sdk.ts b/packages/angular/src/sdk.ts index 404305f770ff..9752db9cc384 100755 --- a/packages/angular/src/sdk.ts +++ b/packages/angular/src/sdk.ts @@ -33,6 +33,8 @@ export function getDefaultIntegrations(_options: BrowserOptions = {}): Integrati // - https://github.com/getsentry/sentry-javascript/issues/5417#issuecomment-1453407097 // - https://github.com/getsentry/sentry-javascript/issues/2744 return [ + // TODO(v10): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration` + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration(), functionToStringIntegration(), breadcrumbsIntegration(), diff --git a/packages/astro/src/index.server.ts b/packages/astro/src/index.server.ts index c7b997c2cb92..6f9647d0134e 100644 --- a/packages/astro/src/index.server.ts +++ b/packages/astro/src/index.server.ts @@ -58,7 +58,9 @@ export { graphqlIntegration, hapiIntegration, httpIntegration, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, initOpenTelemetry, isInitialized, kafkaIntegration, diff --git a/packages/astro/test/server/index.server.test.ts b/packages/astro/test/server/index.server.test.ts index f319ef90eaad..4a89ebe16a13 100644 --- a/packages/astro/test/server/index.server.test.ts +++ b/packages/astro/test/server/index.server.test.ts @@ -1,4 +1,6 @@ +import { describe, expect, it } from 'vitest'; import sentryAstro from '../../src/index.server'; + describe('server SDK', () => { it('exports the astro integration as a default export', () => { const integration = sentryAstro(); diff --git a/packages/astro/test/server/sdk.test.ts b/packages/astro/test/server/sdk.test.ts index 3e571628d29f..ee3808204f5b 100644 --- a/packages/astro/test/server/sdk.test.ts +++ b/packages/astro/test/server/sdk.test.ts @@ -1,6 +1,6 @@ import * as SentryNode from '@sentry/node'; import { SDK_VERSION } from '@sentry/node'; -import { vi } from 'vitest'; +import { vi, describe, afterEach, expect, it } from 'vitest'; import { init } from '../../src/server/sdk'; diff --git a/packages/aws-serverless/jest.config.js b/packages/aws-serverless/jest.config.js deleted file mode 100644 index 24f49ab59a4c..000000000000 --- a/packages/aws-serverless/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../jest/jest.config.js'); diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index dcd7fb1c7b96..7068792f2164 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -65,9 +65,9 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/instrumentation-aws-lambda": "0.50.2", - "@opentelemetry/instrumentation-aws-sdk": "0.49.0", + "@opentelemetry/instrumentation": "^0.57.2", + "@opentelemetry/instrumentation-aws-lambda": "0.50.3", + "@opentelemetry/instrumentation-aws-sdk": "0.49.1", "@sentry/core": "9.2.0", "@sentry/node": "9.2.0", "@types/aws-lambda": "^8.10.62" @@ -93,8 +93,8 @@ "clean": "rimraf build dist-awslambda-layer coverage sentry-serverless-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "jest", - "test:watch": "jest --watch", + "test": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "volta": { diff --git a/packages/aws-serverless/src/index.ts b/packages/aws-serverless/src/index.ts index 2cd5ee5661ec..51848530712b 100644 --- a/packages/aws-serverless/src/index.ts +++ b/packages/aws-serverless/src/index.ts @@ -54,7 +54,9 @@ export { localVariablesIntegration, requestDataIntegration, functionToStringIntegration, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, linkedErrorsIntegration, setMeasurement, getActiveSpan, diff --git a/packages/aws-serverless/test/__mocks__/dns.ts b/packages/aws-serverless/test/__mocks__/dns.ts index d03aa8d3f84b..9084db8be99c 100644 --- a/packages/aws-serverless/test/__mocks__/dns.ts +++ b/packages/aws-serverless/test/__mocks__/dns.ts @@ -1,2 +1,4 @@ -export const lookup = jest.fn(); -export const resolveTxt = jest.fn(); +import { vi } from 'vitest'; + +export const lookup = vi.fn(); +export const resolveTxt = vi.fn(); diff --git a/packages/aws-serverless/test/sdk.test.ts b/packages/aws-serverless/test/sdk.test.ts index 4f68eb0b6edb..bc69a32aed68 100644 --- a/packages/aws-serverless/test/sdk.test.ts +++ b/packages/aws-serverless/test/sdk.test.ts @@ -2,27 +2,29 @@ import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } fr import type { Event } from '@sentry/core'; import type { Callback, Handler } from 'aws-lambda'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; import { init, wrapHandler } from '../src/sdk'; -const mockSpanEnd = jest.fn(); -const mockStartInactiveSpan = jest.fn((...spanArgs) => ({ ...spanArgs })); -const mockStartSpanManual = jest.fn((...spanArgs) => ({ ...spanArgs })); -const mockFlush = jest.fn((...args) => Promise.resolve(args)); -const mockWithScope = jest.fn(); -const mockCaptureMessage = jest.fn(); -const mockCaptureException = jest.fn(); -const mockInit = jest.fn(); +const mockSpanEnd = vi.fn(); +const mockStartInactiveSpan = vi.fn((...spanArgs) => ({ ...spanArgs })); +const mockStartSpanManual = vi.fn((...spanArgs) => ({ ...spanArgs })); +const mockFlush = vi.fn((...args) => Promise.resolve(args)); +const mockWithScope = vi.fn(); +const mockCaptureMessage = vi.fn(); +const mockCaptureException = vi.fn(); +const mockInit = vi.fn(); const mockScope = { - setTag: jest.fn(), - setContext: jest.fn(), - addEventProcessor: jest.fn(), - setTransactionName: jest.fn(), + setTag: vi.fn(), + setContext: vi.fn(), + addEventProcessor: vi.fn(), + setTransactionName: vi.fn(), }; -jest.mock('@sentry/node', () => { - const original = jest.requireActual('@sentry/node'); +vi.mock('@sentry/node', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const original = (await vi.importActual('@sentry/node')) as typeof import('@sentry/node'); return { ...original, initWithoutDefaultIntegrations: (options: unknown) => { @@ -115,7 +117,7 @@ describe('AWSLambda', () => { fortySix: 'o_O', }; - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('wrapHandler() options', () => { @@ -495,7 +497,7 @@ describe('AWSLambda', () => { const scopeFunction = mockCaptureException.mock.calls[0][1]; const event: Event = { exception: { values: [{}] } }; let evtProcessor: ((e: Event) => Event) | undefined = undefined; - scopeFunction({ addEventProcessor: jest.fn().mockImplementation(proc => (evtProcessor = proc)) }); + scopeFunction({ addEventProcessor: vi.fn().mockImplementation(proc => (evtProcessor = proc)) }); expect(evtProcessor).toBeInstanceOf(Function); // @ts-expect-error just mocking around... diff --git a/packages/aws-serverless/test/utils.test.ts b/packages/aws-serverless/test/utils.test.ts index 197c6ebdf90f..8fba98d0add6 100644 --- a/packages/aws-serverless/test/utils.test.ts +++ b/packages/aws-serverless/test/utils.test.ts @@ -1,8 +1,10 @@ +import { afterEach, describe, expect, test, vi } from 'vitest'; import { eventContextExtractor, getAwsTraceData } from '../src/utils'; -const mockExtractContext = jest.fn(); -jest.mock('@opentelemetry/api', () => { - const actualApi = jest.requireActual('@opentelemetry/api'); +const mockExtractContext = vi.fn(); +vi.mock('@opentelemetry/api', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const actualApi = (await vi.importActual('@opentelemetry/api')) as typeof import('@opentelemetry/api'); return { ...actualApi, propagation: { @@ -63,7 +65,7 @@ describe('getTraceData', () => { describe('eventContextExtractor', () => { afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('passes sentry trace data to the propagation extractor', () => { diff --git a/packages/aws-serverless/tsconfig.test.json b/packages/aws-serverless/tsconfig.test.json index 87f6afa06b86..ca7dbeb3be94 100644 --- a/packages/aws-serverless/tsconfig.test.json +++ b/packages/aws-serverless/tsconfig.test.json @@ -1,11 +1,11 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*"], + "include": ["test/**/*", "vite.config.ts"], "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node"] // other package-specific, test-specific options } diff --git a/packages/aws-serverless/vite.config.ts b/packages/aws-serverless/vite.config.ts new file mode 100644 index 000000000000..f18ec92095bc --- /dev/null +++ b/packages/aws-serverless/vite.config.ts @@ -0,0 +1,8 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, + test: { + ...baseConfig.test, + }, +}; diff --git a/packages/browser-utils/test/browser/browserMetrics.test.ts b/packages/browser-utils/test/browser/browserMetrics.test.ts index 27d489eae140..69169e01af67 100644 --- a/packages/browser-utils/test/browser/browserMetrics.test.ts +++ b/packages/browser-utils/test/browser/browserMetrics.test.ts @@ -9,6 +9,8 @@ import { spanToJSON, } from '@sentry/core'; import type { Span } from '@sentry/core'; +import { describe, beforeEach, it, expect, beforeAll, afterAll } from 'vitest'; + import { _addMeasureSpans, _addResourceSpans } from '../../src/metrics/browserMetrics'; import { WINDOW } from '../../src/types'; import { TestClient, getDefaultClientOptions } from '../utils/TestClient'; diff --git a/packages/browser-utils/test/browser/utils.test.ts b/packages/browser-utils/test/browser/utils.test.ts index 01fb5da605c4..9d8a6c301e84 100644 --- a/packages/browser-utils/test/browser/utils.test.ts +++ b/packages/browser-utils/test/browser/utils.test.ts @@ -1,4 +1,6 @@ import { SentrySpan, getCurrentScope, getIsolationScope, setCurrentClient, spanToJSON } from '@sentry/core'; +import { describe, beforeEach, it, expect, test } from 'vitest'; + import { extractNetworkProtocol, startAndEndSpan } from '../../src/metrics/utils'; import { TestClient, getDefaultClientOptions } from '../utils/TestClient'; diff --git a/packages/browser-utils/test/instrument/dom.test.ts b/packages/browser-utils/test/instrument/dom.test.ts index 102c6ba40829..a2c2a12dbf16 100644 --- a/packages/browser-utils/test/instrument/dom.test.ts +++ b/packages/browser-utils/test/instrument/dom.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from 'vitest'; + import { instrumentDOM } from '../../src/instrument/dom'; import { WINDOW } from '../../src/types'; diff --git a/packages/browser-utils/test/instrument/xhr.test.ts b/packages/browser-utils/test/instrument/xhr.test.ts index e53999de628b..352cce016fcb 100644 --- a/packages/browser-utils/test/instrument/xhr.test.ts +++ b/packages/browser-utils/test/instrument/xhr.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, it } from 'vitest'; + import { instrumentXHR } from '../../src/instrument/xhr'; import { WINDOW } from '../../src/types'; diff --git a/packages/browser/src/exports.ts b/packages/browser/src/exports.ts index ae2ff9c3fa87..8745e34106f4 100644 --- a/packages/browser/src/exports.ts +++ b/packages/browser/src/exports.ts @@ -49,7 +49,9 @@ export { withScope, withIsolationScope, functionToStringIntegration, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, dedupeIntegration, parameterize, startSession, @@ -82,13 +84,7 @@ export { } from './stack-parsers'; export { eventFromException, eventFromMessage, exceptionFromError } from './eventbuilder'; export { createUserFeedbackEnvelope } from './userfeedback'; -export { - getDefaultIntegrations, - forceLoad, - init, - onLoad, - showReportDialog, -} from './sdk'; +export { getDefaultIntegrations, forceLoad, init, onLoad, showReportDialog } from './sdk'; export { breadcrumbsIntegration } from './integrations/breadcrumbs'; export { globalHandlersIntegration } from './integrations/globalhandlers'; diff --git a/packages/browser/src/index.ts b/packages/browser/src/index.ts index 63da52dfd30e..d034330b6283 100644 --- a/packages/browser/src/index.ts +++ b/packages/browser/src/index.ts @@ -62,10 +62,7 @@ export { makeBrowserOfflineTransport } from './transports/offline'; export { browserProfilingIntegration } from './profiling/integration'; export { spotlightBrowserIntegration } from './integrations/spotlight'; export { browserSessionIntegration } from './integrations/browsersession'; -export { - featureFlagsIntegration, - type FeatureFlagsIntegration, -} from './integrations/featureFlags'; +export { featureFlagsIntegration, type FeatureFlagsIntegration } from './integrations/featureFlags'; export { launchDarklyIntegration, buildLaunchDarklyFlagUsedHandler } from './integrations/featureFlags/launchdarkly'; export { openFeatureIntegration, OpenFeatureIntegrationHook } from './integrations/featureFlags/openfeature'; export { unleashIntegration } from './integrations/featureFlags/unleash'; diff --git a/packages/browser/src/sdk.ts b/packages/browser/src/sdk.ts index d91f62d1ec47..1c7e6fbe95ad 100644 --- a/packages/browser/src/sdk.ts +++ b/packages/browser/src/sdk.ts @@ -34,6 +34,8 @@ export function getDefaultIntegrations(_options: Options): Integration[] { * `getDefaultIntegrations` but with an adjusted set of integrations. */ return [ + // TODO(v10): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration` + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration(), functionToStringIntegration(), browserApiErrorsIntegration(), diff --git a/packages/browser/test/index.test.ts b/packages/browser/test/index.test.ts index c0e79481788b..815dd39707f9 100644 --- a/packages/browser/test/index.test.ts +++ b/packages/browser/test/index.test.ts @@ -7,10 +7,10 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { SDK_VERSION, + eventFiltersIntegration, getGlobalScope, getIsolationScope, getReportDialogEndpoint, - inboundFiltersIntegration, lastEventId, } from '@sentry/core'; import * as utils from '@sentry/core'; @@ -304,12 +304,12 @@ describe('SentryBrowser', () => { expect(localBeforeSend).toHaveBeenCalledTimes(2); }); - it('should use inboundfilter rules of bound client', async () => { + it('should use eventFilters rules of bound client', async () => { const localBeforeSend = vi.fn(); const options = getDefaultBrowserClientOptions({ beforeSend: localBeforeSend, dsn, - integrations: [inboundFiltersIntegration({ ignoreErrors: ['capture'] })], + integrations: [eventFiltersIntegration({ ignoreErrors: ['capture'] })], }); const client = new BrowserClient(options); setCurrentClient(client); diff --git a/packages/browser/test/loader.js b/packages/browser/test/loader.js index 5361aea71b7a..d7f2aa02fbb9 100644 --- a/packages/browser/test/loader.js +++ b/packages/browser/test/loader.js @@ -1,4 +1,3 @@ -// biome-ignore format: Disabled due to trailing comma not working in IE10/11 (function( _window, _document, diff --git a/packages/browser/test/sdk.test.ts b/packages/browser/test/sdk.test.ts index a6fc49edee89..6f8bdc66f450 100644 --- a/packages/browser/test/sdk.test.ts +++ b/packages/browser/test/sdk.test.ts @@ -4,7 +4,7 @@ /* eslint-disable @typescript-eslint/unbound-method */ import type { Mock } from 'vitest'; -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi, afterAll, test } from 'vitest'; import * as SentryCore from '@sentry/core'; import { createTransport } from '@sentry/core'; diff --git a/packages/browser/test/tracing/request.test.ts b/packages/browser/test/tracing/request.test.ts index 67cd96ee6717..717452a4dff0 100644 --- a/packages/browser/test/tracing/request.test.ts +++ b/packages/browser/test/tracing/request.test.ts @@ -1,5 +1,5 @@ import type { MockInstance } from 'vitest'; -import { beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; +import { beforeAll, beforeEach, describe, expect, it, vi, afterEach } from 'vitest'; import * as browserUtils from '@sentry-internal/browser-utils'; import * as utils from '@sentry/core'; diff --git a/packages/browser/test/transports/offline.test.ts b/packages/browser/test/transports/offline.test.ts index 070d6623f967..7b1cd5ebf6dd 100644 --- a/packages/browser/test/transports/offline.test.ts +++ b/packages/browser/test/transports/offline.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, beforeAll } from 'vitest'; import 'fake-indexeddb/auto'; diff --git a/packages/browser/test/utils/featureFlags.test.ts b/packages/browser/test/utils/featureFlags.test.ts index 48d2f20e3c91..39681a6dfe1f 100644 --- a/packages/browser/test/utils/featureFlags.test.ts +++ b/packages/browser/test/utils/featureFlags.test.ts @@ -1,7 +1,7 @@ import type { FeatureFlag } from '@sentry/core'; import { getCurrentScope, logger } from '@sentry/core'; -import { vi } from 'vitest'; +import { vi, describe, it, afterEach, expect } from 'vitest'; import { insertFlagToScope, insertToFlagBuffer } from '../../src/utils/featureFlags'; describe('flags', () => { diff --git a/packages/bun/jest.config.js b/packages/bun/jest.config.js deleted file mode 100644 index 24f49ab59a4c..000000000000 --- a/packages/bun/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../jest/jest.config.js'); diff --git a/packages/bun/src/index.ts b/packages/bun/src/index.ts index c25c65487a47..52b3d9fa4c42 100644 --- a/packages/bun/src/index.ts +++ b/packages/bun/src/index.ts @@ -74,7 +74,9 @@ export { requestDataIntegration, fsIntegration, functionToStringIntegration, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, linkedErrorsIntegration, setMeasurement, getActiveSpan, diff --git a/packages/bun/src/sdk.ts b/packages/bun/src/sdk.ts index 96f1b63f902d..6c56a66aecea 100644 --- a/packages/bun/src/sdk.ts +++ b/packages/bun/src/sdk.ts @@ -29,6 +29,8 @@ export function getDefaultIntegrations(_options: Options): Integration[] { // We return a copy of the defaultIntegrations here to avoid mutating this return [ // Common + // TODO(v10): Replace with eventFiltersIntegration once we remove the deprecated `inboundFiltersIntegration` + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration(), functionToStringIntegration(), linkedErrorsIntegration(), diff --git a/packages/bun/tsconfig.test.json b/packages/bun/tsconfig.test.json index e5d8bb0fb535..4cbbffaccbbc 100644 --- a/packages/bun/tsconfig.test.json +++ b/packages/bun/tsconfig.test.json @@ -5,7 +5,7 @@ "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["bun-types", "jest"] + "types": ["bun-types"] // other package-specific, test-specific options } diff --git a/packages/cloudflare/package.json b/packages/cloudflare/package.json index 29766fb69658..c833766769ae 100644 --- a/packages/cloudflare/package.json +++ b/packages/cloudflare/package.json @@ -26,6 +26,16 @@ "types": "./build/types/index.d.ts", "default": "./build/cjs/index.js" } + }, + "./request": { + "import": { + "types": "./build/types/request.d.ts", + "default": "./build/esm/request.js" + }, + "require": { + "types": "./build/types/request.d.ts", + "default": "./build/cjs/request.js" + } } }, "typesVersions": { diff --git a/packages/cloudflare/src/index.ts b/packages/cloudflare/src/index.ts index 2aedf4362aea..cd3dda5924ec 100644 --- a/packages/cloudflare/src/index.ts +++ b/packages/cloudflare/src/index.ts @@ -65,7 +65,9 @@ export { getSpanDescendants, continueTrace, functionToStringIntegration, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, linkedErrorsIntegration, requestDataIntegration, extraErrorDataIntegration, diff --git a/packages/cloudflare/src/sdk.ts b/packages/cloudflare/src/sdk.ts index 89f3fe99d050..9891994e8de1 100644 --- a/packages/cloudflare/src/sdk.ts +++ b/packages/cloudflare/src/sdk.ts @@ -20,6 +20,8 @@ export function getDefaultIntegrations(options: CloudflareOptions): Integration[ const sendDefaultPii = options.sendDefaultPii ?? false; return [ dedupeIntegration(), + // TODO(v10): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration` + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration(), functionToStringIntegration(), linkedErrorsIntegration(), diff --git a/packages/core/jest.config.js b/packages/core/jest.config.js deleted file mode 100644 index 24f49ab59a4c..000000000000 --- a/packages/core/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../jest/jest.config.js'); diff --git a/packages/core/package.json b/packages/core/package.json index 1b0786cacfb0..9414ee722d67 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -54,8 +54,8 @@ "clean": "rimraf build coverage sentry-core-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "jest", - "test:watch": "jest --watch", + "test": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "volta": { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index e0e9097bbc53..35bfc35bc603 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -39,10 +39,7 @@ export { getClient, getTraceContextFromScope, } from './currentScopes'; -export { - getDefaultCurrentScope, - getDefaultIsolationScope, -} from './defaultScopes'; +export { getDefaultCurrentScope, getDefaultIsolationScope } from './defaultScopes'; export { setAsyncContextStrategy } from './asyncContext'; export { getGlobalSingleton, getMainCarrier } from './carrier'; export { makeSession, closeSession, updateSession } from './session'; @@ -60,11 +57,7 @@ export { initAndBind, setCurrentClient } from './sdk'; export { createTransport } from './transports/base'; export { makeOfflineTransport } from './transports/offline'; export { makeMultiplexedTransport } from './transports/multiplexed'; -export { - getIntegrationsToSetup, - addIntegration, - defineIntegration, -} from './integration'; +export { getIntegrationsToSetup, addIntegration, defineIntegration } from './integration'; export { applyScopeDataToEvent, mergeScopeData } from './utils/applyScopeDataToEvent'; export { prepareEvent } from './utils/prepareEvent'; export { createCheckInEnvelope } from './checkin'; @@ -103,7 +96,9 @@ export { export { DEFAULT_ENVIRONMENT } from './constants'; export { addBreadcrumb } from './breadcrumbs'; export { functionToStringIntegration } from './integrations/functiontostring'; -export { inboundFiltersIntegration } from './integrations/inboundfilters'; +// eslint-disable-next-line deprecation/deprecation +export { inboundFiltersIntegration } from './integrations/eventFilters'; +export { eventFiltersIntegration } from './integrations/eventFilters'; export { linkedErrorsIntegration } from './integrations/linkederrors'; export { moduleMetadataIntegration } from './integrations/metadata'; export { requestDataIntegration } from './integrations/requestdata'; diff --git a/packages/core/src/integrations/inboundfilters.ts b/packages/core/src/integrations/eventFilters.ts similarity index 71% rename from packages/core/src/integrations/inboundfilters.ts rename to packages/core/src/integrations/eventFilters.ts index 7840ab5f6920..a3fd2f5fb19b 100644 --- a/packages/core/src/integrations/inboundfilters.ts +++ b/packages/core/src/integrations/eventFilters.ts @@ -15,7 +15,7 @@ const DEFAULT_IGNORE_ERRORS = [ /^ResizeObserver loop completed with undelivered notifications.$/, // The browser logs this when a ResizeObserver handler takes a bit longer. Usually this is not an actual issue though. It indicates slowness. /^Cannot redefine property: googletag$/, // This is thrown when google tag manager is used in combination with an ad blocker /^Can't find variable: gmo$/, // Error from Google Search App https://issuetracker.google.com/issues/396043331 - "undefined is not an object (evaluating 'a.L')", // Random error that happens but not actionable or noticeable to end-users. + /^undefined is not an object \(evaluating 'a\.[A-Z]'\)$/, // Random error that happens but not actionable or noticeable to end-users. 'can\'t redefine non-configurable property "solana"', // Probably a browser extension or custom browser (Brave) throwing this error "vv().getRestrictions is not a function. (In 'vv().getRestrictions(1,a)', 'vv().getRestrictions' is undefined)", // Error thrown by GTM, seemingly not affecting end-users "Can't find variable: _AutofillCallbackHandler", // Unactionable error in instagram webview https://developers.facebook.com/community/threads/320013549791141/ @@ -23,8 +23,8 @@ const DEFAULT_IGNORE_ERRORS = [ /^Java exception was raised during method invocation$/, // error from Facebook Mobile browser (https://github.com/getsentry/sentry-javascript/issues/15065) ]; -/** Options for the InboundFilters integration */ -export interface InboundFiltersOptions { +/** Options for the EventFilters integration */ +export interface EventFiltersOptions { allowUrls: Array; denyUrls: Array; ignoreErrors: Array; @@ -33,8 +33,9 @@ export interface InboundFiltersOptions { disableErrorDefaults: boolean; } -const INTEGRATION_NAME = 'InboundFilters'; -const _inboundFiltersIntegration = ((options: Partial = {}) => { +const INTEGRATION_NAME = 'EventFilters'; + +const _eventFiltersIntegration = ((options: Partial = {}) => { return { name: INTEGRATION_NAME, processEvent(event, _hint, client) { @@ -45,12 +46,48 @@ const _inboundFiltersIntegration = ((options: Partial = { }; }) satisfies IntegrationFn; -export const inboundFiltersIntegration = defineIntegration(_inboundFiltersIntegration); +/** + * An integration that filters out events (errors and transactions) based on: + * + * - (Errors) A curated list of known low-value or irrelevant errors (see {@link DEFAULT_IGNORE_ERRORS}) + * - (Errors) A list of error messages or urls/filenames passed in via + * - Top level Sentry.init options (`ignoreErrors`, `denyUrls`, `allowUrls`) + * - The same options passed to the integration directly via @param options + * - (Transactions/Spans) A list of root span (transaction) names passed in via + * - Top level Sentry.init option (`ignoreTransactions`) + * - The same option passed to the integration directly via @param options + * + * Events filtered by this integration will not be sent to Sentry. + */ +export const eventFiltersIntegration = defineIntegration(_eventFiltersIntegration); + +/** + * An integration that filters out events (errors and transactions) based on: + * + * - (Errors) A curated list of known low-value or irrelevant errors (see {@link DEFAULT_IGNORE_ERRORS}) + * - (Errors) A list of error messages or urls/filenames passed in via + * - Top level Sentry.init options (`ignoreErrors`, `denyUrls`, `allowUrls`) + * - The same options passed to the integration directly via @param options + * - (Transactions/Spans) A list of root span (transaction) names passed in via + * - Top level Sentry.init option (`ignoreTransactions`) + * - The same option passed to the integration directly via @param options + * + * Events filtered by this integration will not be sent to Sentry. + * + * @deprecated this integration was renamed and will be removed in a future major version. + * Use `eventFiltersIntegration` instead. + */ +export const inboundFiltersIntegration = defineIntegration(((options: Partial = {}) => { + return { + ...eventFiltersIntegration(options), + name: 'InboundFilters', + }; +}) satisfies IntegrationFn); function _mergeOptions( - internalOptions: Partial = {}, - clientOptions: Partial = {}, -): Partial { + internalOptions: Partial = {}, + clientOptions: Partial = {}, +): Partial { return { allowUrls: [...(internalOptions.allowUrls || []), ...(clientOptions.allowUrls || [])], denyUrls: [...(internalOptions.denyUrls || []), ...(clientOptions.denyUrls || [])], @@ -64,7 +101,7 @@ function _mergeOptions( }; } -function _shouldDropEvent(event: Event, options: Partial): boolean { +function _shouldDropEvent(event: Event, options: Partial): boolean { if (options.ignoreInternal && _isSentryError(event)) { DEBUG_BUILD && logger.warn(`Event dropped due to being internal Sentry Error.\nEvent: ${getEventDescription(event)}`); @@ -172,13 +209,12 @@ function _getLastValidUrl(frames: StackFrame[] = []): string | null { function _getEventFilterUrl(event: Event): string | null { try { - let frames; - try { - // @ts-expect-error we only care about frames if the whole thing here is defined - frames = event.exception.values[0].stacktrace.frames; - } catch (e) { - // ignore - } + // If there are linked exceptions or exception aggregates we only want to match against the top frame of the "root" (the main exception) + // The root always comes last in linked exceptions + const rootException = [...(event.exception?.values ?? []).reverse()]?.find( + value => value.mechanism?.parent_id === undefined && value.stacktrace?.frames?.length, + ); + const frames = rootException?.stacktrace?.frames; return frames ? _getLastValidUrl(frames) : null; } catch (oO) { DEBUG_BUILD && logger.error(`Cannot extract url for event ${getEventDescription(event)}`); diff --git a/packages/core/src/tracing/dynamicSamplingContext.ts b/packages/core/src/tracing/dynamicSamplingContext.ts index f58f373447bb..02e3e3d76e0a 100644 --- a/packages/core/src/tracing/dynamicSamplingContext.ts +++ b/packages/core/src/tracing/dynamicSamplingContext.ts @@ -128,9 +128,7 @@ export function getDynamicSamplingContextFromSpan(span: Span): Readonly { @@ -85,6 +86,7 @@ export function trpcMiddleware(options: SentryTrpcMiddlewareOptions = {}) { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.rpc.trpc', }, + forceTransaction: !!options.forceTransaction, }, async span => { try { diff --git a/packages/core/src/types-hoist/index.ts b/packages/core/src/types-hoist/index.ts index c1cbe5284808..524856d514c9 100644 --- a/packages/core/src/types-hoist/index.ts +++ b/packages/core/src/types-hoist/index.ts @@ -84,11 +84,7 @@ export type { SendFeedbackParams, UserFeedback, } from './feedback'; -export type { - QueryParams, - RequestEventData, - SanitizedRequestData, -} from './request'; +export type { QueryParams, RequestEventData, SanitizedRequestData } from './request'; export type { Runtime } from './runtime'; export type { SdkInfo } from './sdkinfo'; export type { SdkMetadata } from './sdkmetadata'; @@ -119,10 +115,7 @@ export type { StackFrame } from './stackframe'; export type { Stacktrace, StackParser, StackLineParser, StackLineParserFn } from './stacktrace'; export type { PropagationContext, TracePropagationTargets, SerializedTraceData } from './tracing'; export type { StartSpanOptions } from './startSpanOptions'; -export type { - TraceparentData, - TransactionSource, -} from './transaction'; +export type { TraceparentData, TransactionSource } from './transaction'; export type { CustomSamplingContext, SamplingContext } from './samplingcontext'; export type { DurationUnit, diff --git a/packages/core/src/utils-hoist/index.ts b/packages/core/src/utils-hoist/index.ts index 189c2ee363aa..4f22928eff86 100644 --- a/packages/core/src/utils-hoist/index.ts +++ b/packages/core/src/utils-hoist/index.ts @@ -1,10 +1,6 @@ export { applyAggregateErrorsToEvent } from './aggregate-errors'; export { getBreadcrumbLogLevelFromHttpStatusCode } from './breadcrumb-log-level'; -export { - getComponentName, - getLocationHref, - htmlTreeAsString, -} from './browser'; +export { getComponentName, getLocationHref, htmlTreeAsString } from './browser'; export { dsnFromString, dsnToString, makeDsn } from './dsn'; export { SentryError } from './error'; export { GLOBAL_OBJ } from './worldwide'; @@ -13,12 +9,7 @@ export { addConsoleInstrumentationHandler } from './instrument/console'; export { addFetchEndInstrumentationHandler, addFetchInstrumentationHandler } from './instrument/fetch'; export { addGlobalErrorInstrumentationHandler } from './instrument/globalError'; export { addGlobalUnhandledRejectionInstrumentationHandler } from './instrument/globalUnhandledRejection'; -export { - addHandler, - maybeInstrument, - resetInstrumentationHandlers, - triggerHandlers, -} from './instrument/handlers'; +export { addHandler, maybeInstrument, resetInstrumentationHandlers, triggerHandlers } from './instrument/handlers'; export { isDOMError, isDOMException, @@ -88,11 +79,7 @@ export { supportsReportingObserver, } from './supports'; export { SyncPromise, rejectedSyncPromise, resolvedSyncPromise } from './syncpromise'; -export { - browserPerformanceTimeOrigin, - dateTimestampInSeconds, - timestampInSeconds, -} from './time'; +export { browserPerformanceTimeOrigin, dateTimestampInSeconds, timestampInSeconds } from './time'; export { TRACEPARENT_REGEXP, extractTraceparentData, @@ -137,10 +124,7 @@ export { getSanitizedUrlString, parseUrl, stripUrlQueryAndFragment } from './url export { eventFromMessage, eventFromUnknownInput, exceptionFromError, parseStackFrames } from './eventbuilder'; export { callFrameToStackFrame, watchdogTimer } from './anr'; export { LRUMap } from './lru'; -export { - generateTraceId, - generateSpanId, -} from './propagationContext'; +export { generateTraceId, generateSpanId } from './propagationContext'; export { vercelWaitUntil } from './vercelWaitUntil'; export { SDK_VERSION } from './version'; export { getDebugImagesForResources, getFilenameToDebugIdMap } from './debug-ids'; diff --git a/packages/core/src/utils-hoist/misc.ts b/packages/core/src/utils-hoist/misc.ts index 853e11a09894..8a7d26775462 100644 --- a/packages/core/src/utils-hoist/misc.ts +++ b/packages/core/src/utils-hoist/misc.ts @@ -15,15 +15,17 @@ interface CryptoGlobal { crypto?: CryptoInternal; } +function getCrypto(): CryptoInternal | undefined { + const gbl = GLOBAL_OBJ as typeof GLOBAL_OBJ & CryptoGlobal; + return gbl.crypto || gbl.msCrypto; +} + /** * UUID4 generator - * + * @param crypto Object that provides the crypto API. * @returns string Generated UUID4. */ -export function uuid4(): string { - const gbl = GLOBAL_OBJ as typeof GLOBAL_OBJ & CryptoGlobal; - const crypto = gbl.crypto || gbl.msCrypto; - +export function uuid4(crypto = getCrypto()): string { let getRandomByte = (): number => Math.random() * 16; try { if (crypto?.randomUUID) { diff --git a/packages/core/src/utils/request.ts b/packages/core/src/utils/request.ts index 039eff95d3b9..07907d8b9b4f 100644 --- a/packages/core/src/utils/request.ts +++ b/packages/core/src/utils/request.ts @@ -105,7 +105,11 @@ function getAbsoluteUrl({ url, protocol, host, -}: { url?: string; protocol: string; host?: string }): string | undefined { +}: { + url?: string; + protocol: string; + host?: string; +}): string | undefined { if (url?.startsWith('http')) { return url; } diff --git a/packages/core/test/lib/api.test.ts b/packages/core/test/lib/api.test.ts index 37e67e5a8da1..beca401e4858 100644 --- a/packages/core/test/lib/api.test.ts +++ b/packages/core/test/lib/api.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, test } from 'vitest'; import { getEnvelopeEndpointWithUrlEncodedAuth, getReportDialogEndpoint } from '../../src/api'; import type { DsnComponents, SdkInfo } from '../../src/types-hoist'; import { makeDsn } from '../../src/utils-hoist/dsn'; diff --git a/packages/core/test/lib/attachments.test.ts b/packages/core/test/lib/attachments.test.ts index 4ddd0acac6c0..78ea620d9226 100644 --- a/packages/core/test/lib/attachments.test.ts +++ b/packages/core/test/lib/attachments.test.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { createTransport } from '../../src/transports/base'; import { parseEnvelope } from '../../src/utils-hoist/envelope'; import { TestClient, getDefaultTestClientOptions } from '../mocks/client'; @@ -9,7 +10,7 @@ describe('Attachments', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); test('actually end up in envelope', async () => { diff --git a/packages/core/test/lib/carrier.test.ts b/packages/core/test/lib/carrier.test.ts index 19ebc8117ca4..144355dc1d3b 100644 --- a/packages/core/test/lib/carrier.test.ts +++ b/packages/core/test/lib/carrier.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getSentryCarrier } from '../../src/carrier'; import { SDK_VERSION } from '../../src/utils-hoist/version'; diff --git a/packages/core/test/lib/checkin.test.ts b/packages/core/test/lib/checkin.test.ts index 14a699ced3e6..a4cdc434ac94 100644 --- a/packages/core/test/lib/checkin.test.ts +++ b/packages/core/test/lib/checkin.test.ts @@ -2,6 +2,8 @@ import type { SerializedCheckIn } from '../../src/types-hoist'; import { createCheckInEnvelope } from '../../src/checkin'; +import { describe, expect, test } from 'vitest'; + describe('createCheckInEnvelope', () => { test('creates a check in envelope header', () => { const envelope = createCheckInEnvelope( diff --git a/packages/core/test/lib/client.test.ts b/packages/core/test/lib/client.test.ts index c415f1ceb411..9f60226c0de7 100644 --- a/packages/core/test/lib/client.test.ts +++ b/packages/core/test/lib/client.test.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'; import { Scope, SentryError, @@ -27,13 +28,13 @@ const PUBLIC_DSN = 'https://username@domain/123'; // eslint-disable-next-line no-var declare var global: any; -const clientEventFromException = jest.spyOn(TestClient.prototype, 'eventFromException'); -const clientProcess = jest.spyOn(TestClient.prototype as any, '_process'); +const clientEventFromException = vi.spyOn(TestClient.prototype, 'eventFromException'); +const clientProcess = vi.spyOn(TestClient.prototype as any, '_process'); -jest.spyOn(miscModule, 'uuid4').mockImplementation(() => '12312012123120121231201212312012'); -jest.spyOn(loggerModule, 'consoleSandbox').mockImplementation(cb => cb()); -jest.spyOn(stringModule, 'truncate').mockImplementation(str => str); -jest.spyOn(timeModule, 'dateTimestampInSeconds').mockImplementation(() => 2020); +vi.spyOn(miscModule, 'uuid4').mockImplementation(() => '12312012123120121231201212312012'); +vi.spyOn(loggerModule, 'consoleSandbox').mockImplementation(cb => cb()); +vi.spyOn(stringModule, 'truncate').mockImplementation(str => str); +vi.spyOn(timeModule, 'dateTimestampInSeconds').mockImplementation(() => 2020); describe('Client', () => { beforeEach(() => { @@ -46,7 +47,7 @@ describe('Client', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('constructor() / getDsn()', () => { @@ -68,7 +69,7 @@ describe('Client', () => { test('handles being passed an invalid Dsn', () => { // Hide warning logs in the test - jest.spyOn(console, 'error').mockImplementation(() => {}); + vi.spyOn(console, 'error').mockImplementation(() => {}); const options = getDefaultTestClientOptions({ dsn: 'abc' }); const client = new TestClient(options); @@ -80,7 +81,7 @@ describe('Client', () => { describe('constructor() / warnings', () => { test('does not warn for defaults', () => { - const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined); + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); new TestClient(options); @@ -167,7 +168,7 @@ describe('Client', () => { test('it records `buffer_overflow` client discard reason when buffer overflows', () => { const options = getDefaultTestClientOptions({ maxBreadcrumbs: 1 }); const client = new TestClient(options); - const recordLostEventSpy = jest.spyOn(client, 'recordDroppedEvent'); + const recordLostEventSpy = vi.spyOn(client, 'recordDroppedEvent'); setCurrentClient(client); getIsolationScope().setClient(client); client.init(); @@ -181,7 +182,7 @@ describe('Client', () => { }); test('calls `beforeBreadcrumb` and adds the breadcrumb without any changes', () => { - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); const client = new TestClient(options); setCurrentClient(client); @@ -194,7 +195,7 @@ describe('Client', () => { }); test('calls `beforeBreadcrumb` and uses the new one', () => { - const beforeBreadcrumb = jest.fn(() => ({ message: 'changed' })); + const beforeBreadcrumb = vi.fn(() => ({ message: 'changed' })); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); const client = new TestClient(options); setCurrentClient(client); @@ -207,7 +208,7 @@ describe('Client', () => { }); test('calls `beforeBreadcrumb` and discards the breadcrumb when returned `null`', () => { - const beforeBreadcrumb = jest.fn(() => null); + const beforeBreadcrumb = vi.fn(() => null); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); const client = new TestClient(options); setCurrentClient(client); @@ -220,7 +221,7 @@ describe('Client', () => { }); test('`beforeBreadcrumb` gets an access to a hint as a second argument', () => { - const beforeBreadcrumb = jest.fn((breadcrumb, hint) => ({ ...breadcrumb, data: hint.data })); + const beforeBreadcrumb = vi.fn((breadcrumb, hint) => ({ ...breadcrumb, data: hint.data })); const options = getDefaultTestClientOptions({ beforeBreadcrumb }); const client = new TestClient(options); setCurrentClient(client); @@ -347,7 +348,7 @@ describe('Client', () => { }); test('captures logger message', () => { - const logSpy = jest.spyOn(loggerModule.logger, 'log').mockImplementation(() => undefined); + const logSpy = vi.spyOn(loggerModule.logger, 'log').mockImplementation(() => undefined); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options); @@ -392,7 +393,7 @@ describe('Client', () => { test('should call `eventFromException` if input to `captureMessage` is not a primitive', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options); - const spy = jest.spyOn(TestClient.instance!, 'eventFromException'); + const spy = vi.spyOn(TestClient.instance!, 'eventFromException'); client.captureMessage('foo'); client.captureMessage(null as any); @@ -439,7 +440,7 @@ describe('Client', () => { }); test('captures logger message', () => { - const logSpy = jest.spyOn(loggerModule.logger, 'log').mockImplementation(() => undefined); + const logSpy = vi.spyOn(loggerModule.logger, 'log').mockImplementation(() => undefined); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options); @@ -987,7 +988,7 @@ describe('Client', () => { test('calls `beforeSend` and uses original event without any changes', () => { expect.assertions(2); - const beforeSend = jest.fn(event => event); + const beforeSend = vi.fn(event => event); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options); @@ -1000,7 +1001,7 @@ describe('Client', () => { test('calls `beforeSendTransaction` and uses original event without any changes', () => { expect.assertions(2); - const beforeSendTransaction = jest.fn(event => event); + const beforeSendTransaction = vi.fn(event => event); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendTransaction }); const client = new TestClient(options); @@ -1013,7 +1014,7 @@ describe('Client', () => { test('calls `beforeSendSpan` and uses original spans without any changes', () => { expect.assertions(3); - const beforeSendSpan = jest.fn(span => span); + const beforeSendSpan = vi.fn(span => span); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendSpan }); const client = new TestClient(options); @@ -1046,7 +1047,7 @@ describe('Client', () => { }); test('does not modify existing contexts for root span in `beforeSendSpan`', () => { - const beforeSendSpan = jest.fn((span: SpanJSON) => { + const beforeSendSpan = vi.fn((span: SpanJSON) => { return { ...span, data: { @@ -1119,7 +1120,7 @@ describe('Client', () => { test('calls `beforeSendTransaction` and uses the modified event', () => { expect.assertions(2); - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { event.transaction = '/adopt/dont/shop'; return event; }); @@ -1133,7 +1134,7 @@ describe('Client', () => { }); test('calls `beforeSendTransaction` and drops spans', () => { - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { event.spans = [{ span_id: 'span5', trace_id: 'trace1', start_timestamp: 1234 }]; return event; }); @@ -1159,7 +1160,7 @@ describe('Client', () => { test('calls `beforeSendSpan` and uses the modified spans', () => { expect.assertions(4); - const beforeSendSpan = jest.fn(span => { + const beforeSendSpan = vi.fn(span => { span.data = { version: 'bravo' }; return span; }); @@ -1201,11 +1202,11 @@ describe('Client', () => { test('calls `beforeSend` and discards the event', () => { expect.assertions(4); - const beforeSend = jest.fn(() => null); + const beforeSend = vi.fn(() => null); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options); - const captureExceptionSpy = jest.spyOn(client, 'captureException'); - const loggerWarnSpy = jest.spyOn(loggerModule.logger, 'log'); + const captureExceptionSpy = vi.spyOn(client, 'captureException'); + const loggerWarnSpy = vi.spyOn(loggerModule.logger, 'log'); client.captureEvent({ message: 'hello' }); @@ -1220,11 +1221,11 @@ describe('Client', () => { test('calls `beforeSendTransaction` and discards the event', () => { expect.assertions(4); - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendTransaction }); const client = new TestClient(options); - const captureExceptionSpy = jest.spyOn(client, 'captureException'); - const loggerWarnSpy = jest.spyOn(loggerModule.logger, 'log'); + const captureExceptionSpy = vi.spyOn(client, 'captureException'); + const loggerWarnSpy = vi.spyOn(loggerModule.logger, 'log'); client.captureEvent({ transaction: '/dogs/are/great', type: 'transaction' }); @@ -1237,9 +1238,9 @@ describe('Client', () => { }); test('does not discard span and warn when returning null from `beforeSendSpan', () => { - const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined); + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); - const beforeSendSpan = jest.fn(() => null as unknown as SpanJSON); + const beforeSendSpan = vi.fn(() => null as unknown as SpanJSON); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendSpan }); const client = new TestClient(options); @@ -1282,11 +1283,11 @@ describe('Client', () => { expect.assertions(invalidValues.length * 3); for (const val of invalidValues) { - const beforeSend = jest.fn(() => val); + const beforeSend = vi.fn(() => val); // @ts-expect-error we need to test regular-js behavior const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options); - const loggerWarnSpy = jest.spyOn(loggerModule.logger, 'warn'); + const loggerWarnSpy = vi.spyOn(loggerModule.logger, 'warn'); client.captureEvent({ message: 'hello' }); @@ -1303,11 +1304,11 @@ describe('Client', () => { expect.assertions(invalidValues.length * 3); for (const val of invalidValues) { - const beforeSendTransaction = jest.fn(() => val); + const beforeSendTransaction = vi.fn(() => val); // @ts-expect-error we need to test regular-js behavior const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendTransaction }); const client = new TestClient(options); - const loggerWarnSpy = jest.spyOn(loggerModule.logger, 'warn'); + const loggerWarnSpy = vi.spyOn(loggerModule.logger, 'warn'); client.captureEvent({ transaction: '/dogs/are/great', type: 'transaction' }); @@ -1319,131 +1320,123 @@ describe('Client', () => { } }); - test('calls async `beforeSend` and uses original event without any changes', done => { - jest.useFakeTimers(); - expect.assertions(2); + test('calls async `beforeSend` and uses original event without any changes', () => + new Promise(done => { + vi.useFakeTimers(); + expect.assertions(2); - const beforeSend = jest.fn( - async event => - new Promise(resolve => { - setTimeout(() => { - resolve(event); - }, 1); - }), - ); - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options); + const beforeSend = vi.fn( + async event => + new Promise(resolve => { + setTimeout(() => { + resolve(event); + }, 1); + }), + ); + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); + const client = new TestClient(options); - client.captureEvent({ message: 'hello' }); - jest.runOnlyPendingTimers(); + client.captureEvent({ message: 'hello' }); + vi.runOnlyPendingTimers(); - TestClient.sendEventCalled = (event: Event) => { - expect(beforeSend).toHaveBeenCalled(); - expect(event.message).toEqual('hello'); - }; + TestClient.sendEventCalled = (event: Event) => { + expect(beforeSend).toHaveBeenCalled(); + expect(event.message).toEqual('hello'); + done(); + }; - setTimeout(() => { - done(); - }, 5); + vi.runOnlyPendingTimers(); + })); + + test('calls async `beforeSendTransaction` and uses original event without any changes', () => + new Promise(done => { + vi.useFakeTimers(); + expect.assertions(2); + + const beforeSendTransaction = vi.fn( + async event => + new Promise(resolve => { + setTimeout(() => { + resolve(event); + }, 1); + }), + ); + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendTransaction }); + const client = new TestClient(options); - jest.runOnlyPendingTimers(); - }); + client.captureEvent({ transaction: '/dogs/are/great', type: 'transaction' }); + vi.runOnlyPendingTimers(); - test('calls async `beforeSendTransaction` and uses original event without any changes', done => { - jest.useFakeTimers(); - expect.assertions(2); + TestClient.sendEventCalled = (event: Event) => { + expect(beforeSendTransaction).toHaveBeenCalled(); + expect(event.transaction).toBe('/dogs/are/great'); + done(); + }; - const beforeSendTransaction = jest.fn( - async event => - new Promise(resolve => { + vi.runOnlyPendingTimers(); + })); + + test('calls async `beforeSend` and uses the modified event', () => + new Promise(done => { + vi.useFakeTimers(); + expect.assertions(2); + + const beforeSend = vi.fn(async event => { + event.message = 'changed2'; + return new Promise(resolve => { setTimeout(() => { resolve(event); }, 1); - }), - ); - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendTransaction }); - const client = new TestClient(options); - - client.captureEvent({ transaction: '/dogs/are/great', type: 'transaction' }); - jest.runOnlyPendingTimers(); - - TestClient.sendEventCalled = (event: Event) => { - expect(beforeSendTransaction).toHaveBeenCalled(); - expect(event.transaction).toBe('/dogs/are/great'); - }; - - setTimeout(() => { - done(); - }, 5); - - jest.runOnlyPendingTimers(); - }); - - test('calls async `beforeSend` and uses the modified event', done => { - jest.useFakeTimers(); - expect.assertions(2); - - const beforeSend = jest.fn(async event => { - event.message = 'changed2'; - return new Promise(resolve => { - setTimeout(() => { - resolve(event); - }, 1); + }); }); - }); - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); - const client = new TestClient(options); - - client.captureEvent({ message: 'hello' }); - jest.runOnlyPendingTimers(); + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); + const client = new TestClient(options); - TestClient.sendEventCalled = (event: Event) => { - expect(beforeSend).toHaveBeenCalled(); - expect(event.message).toEqual('changed2'); - }; + client.captureEvent({ message: 'hello' }); + vi.runOnlyPendingTimers(); - setTimeout(() => { - done(); - }, 5); + TestClient.sendEventCalled = (event: Event) => { + expect(beforeSend).toHaveBeenCalled(); + expect(event.message).toEqual('changed2'); + done(); + }; - jest.runOnlyPendingTimers(); - }); + vi.runOnlyPendingTimers(); + })); - test('calls async `beforeSendTransaction` and uses the modified event', done => { - jest.useFakeTimers(); - expect.assertions(2); + test('calls async `beforeSendTransaction` and uses the modified event', () => + new Promise(done => { + vi.useFakeTimers(); + expect.assertions(2); - const beforeSendTransaction = jest.fn(async event => { - event.transaction = '/adopt/dont/shop'; - return new Promise(resolve => { - setTimeout(() => { - resolve(event); - }, 1); + const beforeSendTransaction = vi.fn(async event => { + event.transaction = '/adopt/dont/shop'; + return new Promise(resolve => { + setTimeout(() => { + resolve(event); + }, 1); + }); }); - }); - const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendTransaction }); - const client = new TestClient(options); - - client.captureEvent({ transaction: '/dogs/are/great', type: 'transaction' }); - jest.runOnlyPendingTimers(); + const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendTransaction }); + const client = new TestClient(options); - TestClient.sendEventCalled = (event: Event) => { - expect(beforeSendTransaction).toHaveBeenCalled(); - expect(event.transaction).toBe('/adopt/dont/shop'); - }; + client.captureEvent({ transaction: '/dogs/are/great', type: 'transaction' }); + vi.runOnlyPendingTimers(); - setTimeout(() => { - done(); - }, 5); + TestClient.sendEventCalled = (event: Event) => { + expect(beforeSendTransaction).toHaveBeenCalled(); + expect(event.transaction).toBe('/adopt/dont/shop'); + done(); + }; - jest.runOnlyPendingTimers(); - }); + vi.runOnlyPendingTimers(); + })); test('calls async `beforeSend` and discards the event', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); expect.assertions(2); - const beforeSend = jest.fn( + const beforeSend = vi.fn( async () => new Promise(resolve => { setTimeout(() => { @@ -1455,17 +1448,17 @@ describe('Client', () => { const client = new TestClient(options); client.captureEvent({ message: 'hello' }); - jest.runAllTimers(); + vi.runAllTimers(); expect(beforeSend).toHaveBeenCalled(); expect(TestClient.instance!.event).toBeUndefined(); }); test('calls async `beforeSendTransaction` and discards the event', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); expect.assertions(2); - const beforeSendTransaction = jest.fn( + const beforeSendTransaction = vi.fn( async () => new Promise(resolve => { setTimeout(() => { @@ -1477,7 +1470,7 @@ describe('Client', () => { const client = new TestClient(options); client.captureEvent({ transaction: '/dogs/are/great', type: 'transaction' }); - jest.runAllTimers(); + vi.runAllTimers(); expect(beforeSendTransaction).toHaveBeenCalled(); expect(TestClient.instance!.event).toBeUndefined(); @@ -1486,7 +1479,7 @@ describe('Client', () => { test('`beforeSend` gets access to a hint as a second argument', () => { expect.assertions(3); - const beforeSend = jest.fn((event, hint) => ({ ...event, data: hint.data })); + const beforeSend = vi.fn((event, hint) => ({ ...event, data: hint.data })); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSend }); const client = new TestClient(options); @@ -1500,7 +1493,7 @@ describe('Client', () => { test('`beforeSendTransaction` gets access to a hint as a second argument', () => { expect.assertions(3); - const beforeSendTransaction = jest.fn((event, hint) => ({ ...event, data: hint.data })); + const beforeSendTransaction = vi.fn((event, hint) => ({ ...event, data: hint.data })); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, beforeSendTransaction }); const client = new TestClient(options); @@ -1520,7 +1513,7 @@ describe('Client', () => { test('`beforeSend` records dropped events', () => { expect.assertions(2); - const beforeSend = jest.fn(() => null); + const beforeSend = vi.fn(() => null); const client = new TestClient( getDefaultTestClientOptions({ dsn: PUBLIC_DSN, @@ -1528,7 +1521,7 @@ describe('Client', () => { }), ); - const recordLostEventSpy = jest.spyOn(client, 'recordDroppedEvent'); + const recordLostEventSpy = vi.spyOn(client, 'recordDroppedEvent'); client.captureEvent({ message: 'hello' }, {}); @@ -1539,7 +1532,7 @@ describe('Client', () => { test('`beforeSendTransaction` records dropped events', () => { expect.assertions(2); - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); const client = new TestClient( getDefaultTestClientOptions({ @@ -1548,7 +1541,7 @@ describe('Client', () => { }), ); - const recordLostEventSpy = jest.spyOn(client, 'recordDroppedEvent'); + const recordLostEventSpy = vi.spyOn(client, 'recordDroppedEvent'); client.captureEvent({ transaction: '/dogs/are/great', type: 'transaction' }); @@ -1560,8 +1553,8 @@ describe('Client', () => { expect.assertions(3); const client = new TestClient(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })); - const captureExceptionSpy = jest.spyOn(client, 'captureException'); - const loggerLogSpy = jest.spyOn(loggerModule.logger, 'log'); + const captureExceptionSpy = vi.spyOn(client, 'captureException'); + const loggerLogSpy = vi.spyOn(loggerModule.logger, 'log'); const scope = new Scope(); scope.addEventProcessor(() => null); @@ -1578,8 +1571,8 @@ describe('Client', () => { expect.assertions(3); const client = new TestClient(getDefaultTestClientOptions({ dsn: PUBLIC_DSN })); - const captureExceptionSpy = jest.spyOn(client, 'captureException'); - const loggerLogSpy = jest.spyOn(loggerModule.logger, 'log'); + const captureExceptionSpy = vi.spyOn(client, 'captureException'); + const loggerLogSpy = vi.spyOn(loggerModule.logger, 'log'); const scope = new Scope(); scope.addEventProcessor(() => null); @@ -1598,7 +1591,7 @@ describe('Client', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options); - const recordLostEventSpy = jest.spyOn(client, 'recordDroppedEvent'); + const recordLostEventSpy = vi.spyOn(client, 'recordDroppedEvent'); const scope = new Scope(); scope.addEventProcessor(() => null); @@ -1614,7 +1607,7 @@ describe('Client', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options); - const recordLostEventSpy = jest.spyOn(client, 'recordDroppedEvent'); + const recordLostEventSpy = vi.spyOn(client, 'recordDroppedEvent'); const scope = new Scope(); scope.addEventProcessor(() => null); @@ -1650,7 +1643,7 @@ describe('Client', () => { }); test('mutating transaction name with `beforeSendTransaction` sets transaction-name-change metadata', () => { - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { event.transaction = '/adopt/dont/shop'; return event; }); @@ -1677,8 +1670,8 @@ describe('Client', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options); - const captureExceptionSpy = jest.spyOn(client, 'captureException'); - const loggerWarnSpy = jest.spyOn(loggerModule.logger, 'warn'); + const captureExceptionSpy = vi.spyOn(client, 'captureException'); + const loggerWarnSpy = vi.spyOn(loggerModule.logger, 'warn'); const scope = new Scope(); const exception = new Error('sorry'); scope.addEventProcessor(() => { @@ -1707,14 +1700,14 @@ describe('Client', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, sampleRate: 0 }); const client = new TestClient(options); - const recordLostEventSpy = jest.spyOn(client, 'recordDroppedEvent'); + const recordLostEventSpy = vi.spyOn(client, 'recordDroppedEvent'); client.captureEvent({ message: 'hello' }, {}); expect(recordLostEventSpy).toHaveBeenCalledWith('sample_rate', 'error'); }); test('captures logger message', () => { - const logSpy = jest.spyOn(loggerModule.logger, 'log').mockImplementation(() => undefined); + const logSpy = vi.spyOn(loggerModule.logger, 'log').mockImplementation(() => undefined); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); const client = new TestClient(options); @@ -1776,7 +1769,7 @@ describe('Client', () => { const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations: [new TestIntegration()] }); const client = new TestClient(options); // note: not the `Client` method `setupIntegrations`, but the free-standing function which that method calls - const setupIntegrationsHelper = jest.spyOn(integrationModule, 'setupIntegrations'); + const setupIntegrationsHelper = vi.spyOn(integrationModule, 'setupIntegrations'); // it should install the first time, because integrations aren't yet installed... client.init(); @@ -1797,7 +1790,7 @@ describe('Client', () => { describe('flush/close', () => { test('flush', async () => { - jest.useRealTimers(); + vi.useRealTimers(); expect.assertions(4); const { makeTransport, getSendCalled, getSentCount, delay } = makeFakeTransport(1); @@ -1822,7 +1815,7 @@ describe('Client', () => { }); test('flush with some events being processed async', async () => { - jest.useRealTimers(); + vi.useRealTimers(); expect.assertions(4); const { makeTransport, getSendCalled, getSentCount, delay } = makeFakeTransport(300); @@ -1835,7 +1828,7 @@ describe('Client', () => { }), ); - const spy = jest.spyOn(TestClient.instance!, 'eventFromMessage'); + const spy = vi.spyOn(TestClient.instance!, 'eventFromMessage'); spy.mockImplementationOnce( (message, level) => new SyncPromise(resolve => { @@ -1857,7 +1850,7 @@ describe('Client', () => { }); test('close', async () => { - jest.useRealTimers(); + vi.useRealTimers(); expect.assertions(4); const { makeTransport, delay, getSentCount } = makeFakeTransport(300); @@ -1881,7 +1874,7 @@ describe('Client', () => { }); test('multiple concurrent flush calls should just work', async () => { - jest.useRealTimers(); + vi.useRealTimers(); expect.assertions(3); const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN }); @@ -1903,11 +1896,11 @@ describe('Client', () => { describe('sendEvent', () => { beforeEach(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); }); afterEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); }); it('emits `afterSendEvent` when sending an error', async () => { @@ -1919,15 +1912,15 @@ describe('Client', () => { ); // @ts-expect-error Accessing private transport API - const mockSend = jest.spyOn(client._transport, 'send'); + const mockSend = vi.spyOn(client._transport, 'send'); const errorEvent: Event = { message: 'error' }; - const callback = jest.fn(); + const callback = vi.fn(); client.on('afterSendEvent', callback); client.sendEvent(errorEvent); - jest.runAllTimers(); + vi.runAllTimers(); // Wait for two ticks // note that for whatever reason, await new Promise(resolve => setTimeout(resolve, 0)) causes the test to hang await undefined; @@ -1947,15 +1940,15 @@ describe('Client', () => { ); // @ts-expect-error Accessing private transport API - const mockSend = jest.spyOn(client._transport, 'send'); + const mockSend = vi.spyOn(client._transport, 'send'); const transactionEvent: Event = { type: 'transaction', event_id: 'tr1' }; - const callback = jest.fn(); + const callback = vi.fn(); client.on('afterSendEvent', callback); client.sendEvent(transactionEvent); - jest.runAllTimers(); + vi.runAllTimers(); // Wait for two ticks // note that for whatever reason, await new Promise(resolve => setTimeout(resolve, 0)) causes the test to hang await undefined; @@ -1977,17 +1970,17 @@ describe('Client', () => { ); // @ts-expect-error Accessing private transport API - const mockSend = jest.spyOn(client._transport, 'send').mockImplementation(() => { + const mockSend = vi.spyOn(client._transport, 'send').mockImplementation(() => { return Promise.reject('send error'); }); const errorEvent: Event = { message: 'error' }; - const callback = jest.fn(); + const callback = vi.fn(); client.on('afterSendEvent', callback); client.sendEvent(errorEvent); - jest.runAllTimers(); + vi.runAllTimers(); // Wait for two ticks // note that for whatever reason, await new Promise(resolve => setTimeout(resolve, 0)) causes the test to hang await undefined; @@ -2009,17 +2002,17 @@ describe('Client', () => { ); // @ts-expect-error Accessing private transport API - const mockSend = jest.spyOn(client._transport, 'send').mockImplementation(() => { + const mockSend = vi.spyOn(client._transport, 'send').mockImplementation(() => { return Promise.resolve({ statusCode: 200 }); }); const errorEvent: Event = { message: 'error' }; - const callback = jest.fn(); + const callback = vi.fn(); client.on('afterSendEvent', callback); client.sendEvent(errorEvent); - jest.runAllTimers(); + vi.runAllTimers(); // Wait for two ticks // note that for whatever reason, await new Promise(resolve => setTimeout(resolve, 0)) causes the test to hang await undefined; @@ -2148,7 +2141,7 @@ describe('Client', () => { describe('hook removal with `on`', () => { it('should return a cleanup function that, when executed, unregisters a hook', async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); expect.assertions(8); const client = new TestClient( @@ -2158,19 +2151,19 @@ describe('Client', () => { }), ); - const mockSend = jest.spyOn(client.getTransport()!, 'send').mockImplementation(() => { + const mockSend = vi.spyOn(client.getTransport()!, 'send').mockImplementation(() => { return Promise.resolve({ statusCode: 200 }); }); const errorEvent: Event = { message: 'error' }; - const callback = jest.fn(); + const callback = vi.fn(); const removeAfterSendEventListenerFn = client.on('afterSendEvent', callback); expect(client['_hooks']['afterSendEvent']).toEqual([callback]); client.sendEvent(errorEvent); - jest.runAllTimers(); + vi.runAllTimers(); // Wait for two ticks // note that for whatever reason, await new Promise(resolve => setTimeout(resolve, 0)) causes the test to hang await undefined; @@ -2185,7 +2178,7 @@ describe('Client', () => { expect(client['_hooks']['afterSendEvent']).toEqual([]); client.sendEvent(errorEvent); - jest.runAllTimers(); + vi.runAllTimers(); // Wait for two ticks // note that for whatever reason, await new Promise(resolve => setTimeout(resolve, 0)) causes the test to hang await undefined; @@ -2202,7 +2195,7 @@ describe('Client', () => { describe('withMonitor', () => { test('handles successful synchronous operations', () => { const result = 'foo'; - const callback = jest.fn().mockReturnValue(result); + const callback = vi.fn().mockReturnValue(result); const returnedResult = withMonitor('test-monitor', callback); @@ -2212,7 +2205,7 @@ describe('Client', () => { test('handles synchronous errors', () => { const error = new Error('Test error'); - const callback = jest.fn().mockImplementation(() => { + const callback = vi.fn().mockImplementation(() => { throw error; }); @@ -2221,7 +2214,7 @@ describe('Client', () => { test('handles successful asynchronous operations', async () => { const result = 'foo'; - const callback = jest.fn().mockResolvedValue(result); + const callback = vi.fn().mockResolvedValue(result); const promise = withMonitor('test-monitor', callback); await expect(promise).resolves.toEqual(result); @@ -2236,7 +2229,7 @@ describe('Client', () => { // eslint-disable-next-line @sentry-internal/sdk/no-skipped-tests test.skip('handles asynchronous errors', async () => { const error = new Error('Test error'); - const callback = jest.fn().mockRejectedValue(error); + const callback = vi.fn().mockRejectedValue(error); const promise = await withMonitor('test-monitor', callback); await expect(promise).rejects.toThrowError(error); diff --git a/packages/core/test/lib/envelope.test.ts b/packages/core/test/lib/envelope.test.ts index 81e299ada752..5eb45a495b32 100644 --- a/packages/core/test/lib/envelope.test.ts +++ b/packages/core/test/lib/envelope.test.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import type { Client } from '../../src'; import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, @@ -97,11 +98,11 @@ describe('createSpanEnvelope', () => { client.init(); // We want to avoid console errors in the tests - jest.spyOn(console, 'error').mockImplementation(() => {}); + vi.spyOn(console, 'error').mockImplementation(() => {}); }); afterEach(() => { - jest.resetAllMocks(); + vi.resetAllMocks(); }); it('creates a span envelope', () => { @@ -192,7 +193,7 @@ describe('createSpanEnvelope', () => { }); it('calls `beforeSendSpan` and uses original span without any changes', () => { - const beforeSendSpan = jest.fn(span => span); + const beforeSendSpan = vi.fn(span => span); const options = getDefaultTestClientOptions({ dsn: 'https://domain/123', beforeSendSpan }); const client = new TestClient(options); @@ -224,7 +225,7 @@ describe('createSpanEnvelope', () => { }); it('calls `beforeSendSpan` and uses the modified span', () => { - const beforeSendSpan = jest.fn(span => { + const beforeSendSpan = vi.fn(span => { span.description = `mutated description: ${span.description}`; return span; }); diff --git a/packages/core/test/lib/feedback.test.ts b/packages/core/test/lib/feedback.test.ts index 717329d0b0e0..e82814437d2b 100644 --- a/packages/core/test/lib/feedback.test.ts +++ b/packages/core/test/lib/feedback.test.ts @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it, test, vi } from 'vitest'; import { Scope, addBreadcrumb, @@ -35,7 +36,7 @@ describe('captureFeedback', () => { setCurrentClient(client); client.init(); - const mockTransport = jest.spyOn(client.getTransport()!, 'send'); + const mockTransport = vi.spyOn(client.getTransport()!, 'send'); const eventId = captureFeedback({ message: 'test', @@ -90,7 +91,7 @@ describe('captureFeedback', () => { setCurrentClient(client); client.init(); - const mockTransport = jest.spyOn(client.getTransport()!, 'send'); + const mockTransport = vi.spyOn(client.getTransport()!, 'send'); const eventId = captureFeedback({ name: 'doe', @@ -155,7 +156,7 @@ describe('captureFeedback', () => { setCurrentClient(client); client.init(); - const mockTransport = jest.spyOn(client.getTransport()!, 'send'); + const mockTransport = vi.spyOn(client.getTransport()!, 'send'); const attachment1 = new Uint8Array([1, 2, 3, 4, 5]); const attachment2 = new Uint8Array([6, 7, 8, 9]); @@ -248,7 +249,7 @@ describe('captureFeedback', () => { setCurrentClient(client); client.init(); - const mockTransport = jest.spyOn(client.getTransport()!, 'send'); + const mockTransport = vi.spyOn(client.getTransport()!, 'send'); const traceId = '4C79F60C11214EB38604F4AE0781BFB2'; const spanId = 'FA90FDEAD5F74052'; @@ -324,7 +325,7 @@ describe('captureFeedback', () => { setCurrentClient(client); client.init(); - const mockTransport = jest.spyOn(client.getTransport()!, 'send'); + const mockTransport = vi.spyOn(client.getTransport()!, 'send'); let span: Span | undefined; const eventId = startSpan({ name: 'test-span' }, _span => { @@ -395,7 +396,7 @@ describe('captureFeedback', () => { setCurrentClient(client); client.init(); - const mockTransport = jest.spyOn(client.getTransport()!, 'send'); + const mockTransport = vi.spyOn(client.getTransport()!, 'send'); withIsolationScope(isolationScope => { isolationScope.setTag('test-1', 'tag'); @@ -481,8 +482,8 @@ describe('captureFeedback', () => { const scope = new Scope(); scope.setClient(client2); - const mockTransport = jest.spyOn(client.getTransport()!, 'send'); - const mockTransport2 = jest.spyOn(client2.getTransport()!, 'send'); + const mockTransport = vi.spyOn(client.getTransport()!, 'send'); + const mockTransport2 = vi.spyOn(client2.getTransport()!, 'send'); const eventId = captureFeedback( { diff --git a/packages/core/test/lib/hint.test.ts b/packages/core/test/lib/hint.test.ts index f7fd5ff83ae4..09e1cac8bc2c 100644 --- a/packages/core/test/lib/hint.test.ts +++ b/packages/core/test/lib/hint.test.ts @@ -1,10 +1,11 @@ +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { GLOBAL_OBJ, captureEvent, getCurrentScope } from '../../src'; import { initAndBind } from '../../src/sdk'; import { TestClient, getDefaultTestClientOptions } from '../mocks/client'; import { AddAttachmentTestIntegration } from '../mocks/integration'; const PUBLIC_DSN = 'https://username@domain/123'; -const sendEvent = jest.spyOn(TestClient.prototype, 'sendEvent'); +const sendEvent = vi.spyOn(TestClient.prototype, 'sendEvent'); describe('Hint', () => { beforeEach(() => { @@ -13,7 +14,7 @@ describe('Hint', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); delete GLOBAL_OBJ.__SENTRY__; }); diff --git a/packages/core/test/lib/integration.test.ts b/packages/core/test/lib/integration.test.ts index aa4be2432699..b495c73b85e4 100644 --- a/packages/core/test/lib/integration.test.ts +++ b/packages/core/test/lib/integration.test.ts @@ -1,6 +1,7 @@ import { getCurrentScope } from '../../src/currentScopes'; import type { Integration, Options } from '../../src/types-hoist'; +import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'; import { addIntegration, getIntegrationsToSetup, installedIntegrations, setupIntegration } from '../../src/integration'; import { setCurrentClient } from '../../src/sdk'; import { logger } from '../../src/utils-hoist/logger'; @@ -293,7 +294,7 @@ describe('setupIntegration', () => { it('works with a minimal integration', () => { class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); + setupOnce = vi.fn(); } const client = getTestClient(); @@ -309,7 +310,7 @@ describe('setupIntegration', () => { it('only calls setupOnce a single time', () => { class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); + setupOnce = vi.fn(); } const client1 = getTestClient(); @@ -336,8 +337,8 @@ describe('setupIntegration', () => { it('calls setup for each client', () => { class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); - setup = jest.fn(); + setupOnce = vi.fn(); + setup = vi.fn(); } const client1 = getTestClient(); @@ -374,8 +375,8 @@ describe('setupIntegration', () => { it('binds preprocessEvent for each client', () => { class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); - preprocessEvent = jest.fn(); + setupOnce = vi.fn(); + preprocessEvent = vi.fn(); } const client1 = getTestClient(); @@ -426,8 +427,8 @@ describe('setupIntegration', () => { it('allows to mutate events in preprocessEvent', async () => { class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); - preprocessEvent = jest.fn(event => { + setupOnce = vi.fn(); + preprocessEvent = vi.fn(event => { event.event_id = 'mutated'; }); } @@ -439,7 +440,7 @@ describe('setupIntegration', () => { setupIntegration(client, integration, integrationIndex); - const sendEvent = jest.fn(); + const sendEvent = vi.fn(); client.sendEvent = sendEvent; client.captureEvent({ event_id: '1a' }); @@ -454,8 +455,8 @@ describe('setupIntegration', () => { it('binds processEvent for each client', () => { class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); - processEvent = jest.fn(event => { + setupOnce = vi.fn(); + processEvent = vi.fn(event => { return event; }); } @@ -508,8 +509,8 @@ describe('setupIntegration', () => { it('allows to mutate events in processEvent', async () => { class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); - processEvent = jest.fn(_event => { + setupOnce = vi.fn(); + processEvent = vi.fn(_event => { return { event_id: 'mutated' }; }); } @@ -521,7 +522,7 @@ describe('setupIntegration', () => { setupIntegration(client, integration, integrationIndex); - const sendEvent = jest.fn(); + const sendEvent = vi.fn(); client.sendEvent = sendEvent; client.captureEvent({ event_id: '1a' }); @@ -536,8 +537,8 @@ describe('setupIntegration', () => { it('allows to drop events in processEvent', async () => { class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); - processEvent = jest.fn(_event => { + setupOnce = vi.fn(); + processEvent = vi.fn(_event => { return null; }); } @@ -549,7 +550,7 @@ describe('setupIntegration', () => { setupIntegration(client, integration, integrationIndex); - const sendEvent = jest.fn(); + const sendEvent = vi.fn(); client.sendEvent = sendEvent; client.captureEvent({ event_id: '1a' }); @@ -566,15 +567,15 @@ describe('addIntegration', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('works with a client setup', () => { - const warnings = jest.spyOn(logger, 'warn'); + const warnings = vi.spyOn(logger, 'warn'); class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); + setupOnce = vi.fn(); } const client = getTestClient(); @@ -588,10 +589,10 @@ describe('addIntegration', () => { }); it('works without a client setup', () => { - const warnings = jest.spyOn(logger, 'warn'); + const warnings = vi.spyOn(logger, 'warn'); class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); + setupOnce = vi.fn(); } getCurrentScope().setClient(undefined); @@ -605,9 +606,9 @@ describe('addIntegration', () => { }); it('triggers all hooks', () => { - const setup = jest.fn(); - const setupOnce = jest.fn(); - const setupAfterAll = jest.fn(); + const setup = vi.fn(); + const setupOnce = vi.fn(); + const setupAfterAll = vi.fn(); class CustomIntegration implements Integration { name = 'test'; @@ -629,13 +630,13 @@ describe('addIntegration', () => { }); it('does not trigger hooks if already installed', () => { - const logs = jest.spyOn(logger, 'log'); + const logs = vi.spyOn(logger, 'log'); class CustomIntegration implements Integration { name = 'test'; - setupOnce = jest.fn(); - setup = jest.fn(); - afterAllSetup = jest.fn(); + setupOnce = vi.fn(); + setup = vi.fn(); + afterAllSetup = vi.fn(); } const client = getTestClient(); diff --git a/packages/core/test/lib/integrations/captureconsole.test.ts b/packages/core/test/lib/integrations/captureconsole.test.ts index cea4075f4d5e..b64e6bdd4cac 100644 --- a/packages/core/test/lib/integrations/captureconsole.test.ts +++ b/packages/core/test/lib/integrations/captureconsole.test.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/unbound-method */ +import { type Mock, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import type { Client } from '../../../src'; import * as CurrentScopes from '../../../src/currentScopes'; import * as SentryCore from '../../../src/exports'; @@ -10,14 +11,14 @@ import { resetInstrumentationHandlers } from '../../../src/utils-hoist/instrumen import { CONSOLE_LEVELS, originalConsoleMethods } from '../../../src/utils-hoist/logger'; import { GLOBAL_OBJ } from '../../../src/utils-hoist/worldwide'; -const mockConsole: { [key in ConsoleLevel]: jest.Mock } = { - debug: jest.fn(), - log: jest.fn(), - warn: jest.fn(), - error: jest.fn(), - assert: jest.fn(), - info: jest.fn(), - trace: jest.fn(), +const mockConsole: { [key in ConsoleLevel]: Mock } = { + debug: vi.fn(), + log: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + assert: vi.fn(), + info: vi.fn(), + trace: vi.fn(), }; describe('CaptureConsole setup', () => { @@ -28,23 +29,23 @@ describe('CaptureConsole setup', () => { let mockClient: Client; const mockScope = { - setExtra: jest.fn(), - addEventProcessor: jest.fn(), + setExtra: vi.fn(), + addEventProcessor: vi.fn(), }; - const captureMessage = jest.fn(); - const captureException = jest.fn(); - const withScope = jest.fn(callback => { + const captureMessage = vi.fn(); + const captureException = vi.fn(); + const withScope = vi.fn(callback => { return callback(mockScope); }); beforeEach(() => { mockClient = {} as Client; - jest.spyOn(SentryCore, 'captureMessage').mockImplementation(captureMessage); - jest.spyOn(SentryCore, 'captureException').mockImplementation(captureException); - jest.spyOn(CurrentScopes, 'getClient').mockImplementation(() => mockClient); - jest.spyOn(CurrentScopes, 'withScope').mockImplementation(withScope); + vi.spyOn(SentryCore, 'captureMessage').mockImplementation(captureMessage); + vi.spyOn(SentryCore, 'captureException').mockImplementation(captureException); + vi.spyOn(CurrentScopes, 'getClient').mockImplementation(() => mockClient); + vi.spyOn(CurrentScopes, 'withScope').mockImplementation(withScope); CONSOLE_LEVELS.forEach(key => { originalConsoleMethods[key] = mockConsole[key]; @@ -52,7 +53,7 @@ describe('CaptureConsole setup', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); CONSOLE_LEVELS.forEach(key => { originalConsoleMethods[key] = _originalConsoleMethods[key]; @@ -132,7 +133,7 @@ describe('CaptureConsole setup', () => { expect(mockScope.addEventProcessor).toHaveBeenCalledTimes(1); - const addedEventProcessor = (mockScope.addEventProcessor as jest.Mock).mock.calls[0][0]; + const addedEventProcessor = (mockScope.addEventProcessor as Mock).mock.calls[0]?.[0]; const someEvent: Event = {}; addedEventProcessor(someEvent); @@ -261,7 +262,7 @@ describe('CaptureConsole setup', () => { it('should call the original console function when console members are called', () => { // Mock console log to test if it was called const originalConsoleLog = GLOBAL_OBJ.console.log; - const mockConsoleLog = jest.fn(); + const mockConsoleLog = vi.fn(); GLOBAL_OBJ.console.log = mockConsoleLog; const captureConsole = captureConsoleIntegration({ levels: ['log'] }); @@ -313,7 +314,7 @@ describe('CaptureConsole setup', () => { const someError = new Error('some error'); GLOBAL_OBJ.console.error(someError); - const addedEventProcessor = (mockScope.addEventProcessor as jest.Mock).mock.calls[0][0]; + const addedEventProcessor = (mockScope.addEventProcessor as Mock).mock.calls[0]?.[0]; const someEvent: Event = { exception: { values: [{}], @@ -337,7 +338,7 @@ describe('CaptureConsole setup', () => { const someError = new Error('some error'); GLOBAL_OBJ.console.error(someError); - const addedEventProcessor = (mockScope.addEventProcessor as jest.Mock).mock.calls[0][0]; + const addedEventProcessor = (mockScope.addEventProcessor as Mock).mock.calls[0]?.[0]; const someEvent: Event = { exception: { values: [{}], @@ -361,7 +362,7 @@ describe('CaptureConsole setup', () => { const someError = new Error('some error'); GLOBAL_OBJ.console.error(someError); - const addedEventProcessor = (mockScope.addEventProcessor as jest.Mock).mock.calls[0][0]; + const addedEventProcessor = (mockScope.addEventProcessor as Mock).mock.calls[0]?.[0]; const someEvent: Event = { exception: { values: [{}], diff --git a/packages/core/test/lib/integrations/dedupe.test.ts b/packages/core/test/lib/integrations/dedupe.test.ts index 2e7b10fe1c4c..ea42d99ab3e5 100644 --- a/packages/core/test/lib/integrations/dedupe.test.ts +++ b/packages/core/test/lib/integrations/dedupe.test.ts @@ -2,6 +2,8 @@ import type { Event as SentryEvent, Exception, StackFrame, Stacktrace } from '.. import { _shouldDropEvent, dedupeIntegration } from '../../../src/integrations/dedupe'; +import { describe, expect, it } from 'vitest'; + type EventWithException = SentryEvent & { exception: { values: ExceptionWithStacktrace[]; diff --git a/packages/core/test/lib/integrations/inboundfilters.test.ts b/packages/core/test/lib/integrations/eventFilters.test.ts similarity index 62% rename from packages/core/test/lib/integrations/inboundfilters.test.ts rename to packages/core/test/lib/integrations/eventFilters.test.ts index e06c6bda0da2..51a15f909f5d 100644 --- a/packages/core/test/lib/integrations/inboundfilters.test.ts +++ b/packages/core/test/lib/integrations/eventFilters.test.ts @@ -1,22 +1,24 @@ -import type { Event, EventProcessor } from '../../../src/types-hoist'; +import type { Event, EventProcessor, Integration } from '../../../src/types-hoist'; -import type { InboundFiltersOptions } from '../../../src/integrations/inboundfilters'; -import { inboundFiltersIntegration } from '../../../src/integrations/inboundfilters'; +import { describe, expect, it } from 'vitest'; +import type { EventFiltersOptions } from '../../../src/integrations/eventFilters'; +import { eventFiltersIntegration } from '../../../src/integrations/eventFilters'; +import { inboundFiltersIntegration } from '../../../src/integrations/eventFilters'; import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; const PUBLIC_DSN = 'https://username@domain/123'; /** - * Creates an instance of the InboundFilters integration and returns - * the event processor that the InboundFilters integration creates. + * Creates an instance of the eventFiltersIntegration and returns + * the event processor that the eventFiltersIntegration creates. * - * To test the InboundFilters integration, call this function and assert on + * To test the eventFiltersIntegration, call this function and assert on * how the event processor handles an event. For example, if you set up the - * InboundFilters to filter out an SOME_EXCEPTION_EVENT. + * event filters to filter out an SOME_EXCEPTION_EVENT. * * ``` * // some options that cause SOME_EXCEPTION_EVENT to be filtered - * const eventProcessor = createInboundFiltersEventProcessor(options); + * const eventProcessor = eventFiltersIntegration(options); * * expect(eventProcessor(SOME_EXCEPTION_EVENT)).toBe(null); * ``` @@ -24,23 +26,26 @@ const PUBLIC_DSN = 'https://username@domain/123'; * @param options options passed into the InboundFilters integration * @param clientOptions options passed into the mock Sentry client */ -function createInboundFiltersEventProcessor( - options: Partial = {}, - clientOptions: Partial = {}, +function createEventFiltersEventProcessor( + integrationFn: (opts: Partial) => Integration, + options: Partial = {}, + clientOptions: Partial = {}, ): EventProcessor { + const integration = integrationFn(options); + const client = new TestClient( getDefaultTestClientOptions({ dsn: PUBLIC_DSN, ...clientOptions, defaultIntegrations: false, - integrations: [inboundFiltersIntegration(options)], + integrations: [integration], }), ); client.init(); const eventProcessors = client['_eventProcessors']; - const eventProcessor = eventProcessors.find(processor => processor.id === 'InboundFilters'); + const eventProcessor = eventProcessors.find(processor => processor.id === integration.name); expect(eventProcessor).toBeDefined(); return eventProcessor!; @@ -156,10 +161,91 @@ const EXCEPTION_EVENT_WITH_LINKED_ERRORS: Event = { { type: 'ReferenceError', value: '`tooManyTreats` is not defined', + stacktrace: { + frames: [{ filename: 'https://secondary-error.com/' }], + }, + }, + { + type: 'TypeError', + value: 'incorrect type given for parameter `chewToy`: Shoe', + stacktrace: { + frames: [{ filename: 'https://main-error.com/' }], + }, + }, + ], + }, +}; + +const EXCEPTION_EVENT_WITH_AGGREGATE_ERRORS: Event = { + exception: { + values: [ + { + type: 'ReferenceError', + value: '`tooManyTreats` is not defined', + stacktrace: { + frames: [{ filename: 'https://secondary-error.com/' }], + }, + mechanism: { + type: 'generic', + exception_id: 1, + parent_id: 0, + }, + }, + { + type: 'TypeError', + value: 'incorrect type given for parameter `chewToy`: Shoe', + stacktrace: { + frames: [{ filename: 'https://main-error.com/' }], + }, + mechanism: { + type: 'generic', + exception_id: 0, + }, + }, + ], + }, +}; + +const EXCEPTION_EVENT_WITH_LINKED_ERRORS_WITHOUT_STACKTRACE: Event = { + exception: { + values: [ + { + type: 'ReferenceError', + value: '`tooManyTreats` is not defined', + stacktrace: { + frames: [{ filename: 'https://main-error.com/' }], + }, + }, + { + type: 'TypeError', + value: 'incorrect type given for parameter `chewToy`: Shoe', + }, + ], + }, +}; + +const EXCEPTION_EVENT_WITH_AGGREGATE_ERRORS_WITHOUT_STACKTRACE: Event = { + exception: { + values: [ + { + type: 'ReferenceError', + value: '`tooManyTreats` is not defined', + stacktrace: { + frames: [{ filename: 'https://secondary-error.com/' }], + }, + mechanism: { + type: 'generic', + exception_id: 1, + parent_id: 0, + }, }, { type: 'TypeError', value: 'incorrect type given for parameter `chewToy`: Shoe', + mechanism: { + type: 'generic', + exception_id: 0, + }, }, ], }, @@ -326,17 +412,29 @@ const TRANSACTION_EVENT_3: Event = { type: 'transaction', }; -describe('InboundFilters', () => { +function createUndefinedIsNotAnObjectEvent(evaluatingStr: string): Event { + return { + exception: { + values: [{ type: 'TypeError', value: `undefined is not an object (evaluating '${evaluatingStr}')` }], + }, + }; +} + +describe.each([ + // eslint-disable-next-line deprecation/deprecation + ['InboundFilters', inboundFiltersIntegration], + ['EventFilters', eventFiltersIntegration], +])('%s', (_, integrationFn) => { describe('_isSentryError', () => { it('should work as expected', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT); expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(EXCEPTION_EVENT); expect(eventProcessor(SENTRY_EVENT, {})).toBe(null); }); it('should be configurable', () => { - const eventProcessor = createInboundFiltersEventProcessor({ ignoreInternal: false }); + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreInternal: false }); expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT); expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(EXCEPTION_EVENT); expect(eventProcessor(SENTRY_EVENT, {})).toBe(SENTRY_EVENT); @@ -345,35 +443,35 @@ describe('InboundFilters', () => { describe('ignoreErrors', () => { it('string filter with partial match', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: ['capture'], }); expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null); }); it('ignores transaction event for filtering', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: ['transaction'], }); expect(eventProcessor(TRANSACTION_EVENT, {})).toBe(TRANSACTION_EVENT); }); it('string filter with exact match', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: ['captureMessage'], }); expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null); }); it('regexp filter with partial match', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: [/capture/], }); expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null); }); it('regexp filter with exact match', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: [/^captureMessage$/], }); expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null); @@ -381,7 +479,7 @@ describe('InboundFilters', () => { }); it('prefers message when both message and exception are available', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: [/captureMessage/], }); const event = { @@ -392,7 +490,7 @@ describe('InboundFilters', () => { }); it('can use multiple filters', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: ['captureMessage', /SyntaxError/], }); expect(eventProcessor(MESSAGE_EVENT, {})).toBe(null); @@ -400,44 +498,66 @@ describe('InboundFilters', () => { }); it('uses default filters (script error)', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(SCRIPT_ERROR_EVENT, {})).toBe(null); }); it('uses default filters (ResizeObserver)', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(RESIZEOBSERVER_EVENT, {})).toBe(null); }); it('uses default filters (googletag)', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(GOOGLETAG_EVENT, {})).toBe(null); }); it('uses default filters (Google App "gmo")', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(GOOGLE_APP_GMO, {})).toBe(null); }); it('uses default filters (CEFSharp)', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(CEFSHARP_EVENT, {})).toBe(null); }); it('uses default filters (FB Mobile Browser)', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(FB_MOBILE_BROWSER_EVENT, {})).toBe(null); }); + it("uses default filters (undefined is not an object (evaluating 'a.L'))", () => { + const eventProcessor = createEventFiltersEventProcessor(integrationFn); + expect(eventProcessor(createUndefinedIsNotAnObjectEvent('a.L'), {})).toBe(null); + }); + + it("uses default filters (undefined is not an object (evaluating 'a.K'))", () => { + const eventProcessor = createEventFiltersEventProcessor(integrationFn); + expect(eventProcessor(createUndefinedIsNotAnObjectEvent('a.K'), {})).toBe(null); + }); + + it("doesn't use default filters for (undefined is not an object (evaluating 'a.store'))", () => { + const eventProcessor = createEventFiltersEventProcessor(integrationFn); + const event = createUndefinedIsNotAnObjectEvent('a.store'); + expect(eventProcessor(event, {})).toBe(event); + }); + + it("doesn't use default filters for (undefined is not an object (evaluating 'this._perf.domInteractive'))", () => { + const eventProcessor = createEventFiltersEventProcessor(integrationFn); + const event = createUndefinedIsNotAnObjectEvent('a.store'); + expect(eventProcessor(event, {})).toBe(event); + }); + it('filters on last exception when multiple present', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: ['incorrect type given for parameter `chewToy`'], }); expect(eventProcessor(EXCEPTION_EVENT_WITH_LINKED_ERRORS, {})).toBe(null); }); it("doesn't filter on `cause` exception when multiple present", () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: ['`tooManyTreats` is not defined'], }); expect(eventProcessor(EXCEPTION_EVENT_WITH_LINKED_ERRORS, {})).toBe(EXCEPTION_EVENT_WITH_LINKED_ERRORS); @@ -445,31 +565,31 @@ describe('InboundFilters', () => { describe('on exception', () => { it('uses exception data when message is unavailable', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: ['SyntaxError: unidentified ? at line 1337'], }); expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(null); }); it('can match on exception value', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: [/unidentified \?/], }); expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(null); }); it('can match on exception type', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: [/^SyntaxError/], }); expect(eventProcessor(EXCEPTION_EVENT, {})).toBe(null); }); it('should consider both `event.message` and the last exceptions `type` and `value`', () => { - const messageEventProcessor = createInboundFiltersEventProcessor({ + const messageEventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: [/^ChunkError/], }); - const valueEventProcessor = createInboundFiltersEventProcessor({ + const valueEventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreErrors: [/^SyntaxError/], }); expect(messageEventProcessor(EXCEPTION_EVENT_WITH_MESSAGE_AND_VALUE, {})).toBe(null); @@ -480,35 +600,35 @@ describe('InboundFilters', () => { describe('ignoreTransactions', () => { it('string filter with partial match', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreTransactions: ['name'], }); expect(eventProcessor(TRANSACTION_EVENT, {})).toBe(null); }); it('ignores error event for filtering', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreTransactions: ['capture'], }); expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT); }); it('string filter with exact match', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreTransactions: ['transaction name'], }); expect(eventProcessor(TRANSACTION_EVENT, {})).toBe(null); }); it('regexp filter with partial match', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreTransactions: [/name/], }); expect(eventProcessor(TRANSACTION_EVENT, {})).toBe(null); }); it('regexp filter with exact match', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreTransactions: [/^transaction name$/], }); expect(eventProcessor(TRANSACTION_EVENT, {})).toBe(null); @@ -516,7 +636,7 @@ describe('InboundFilters', () => { }); it('can use multiple filters', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { ignoreTransactions: ['transaction name 2', /transaction/], }); expect(eventProcessor(TRANSACTION_EVENT, {})).toBe(null); @@ -525,27 +645,27 @@ describe('InboundFilters', () => { }); it('uses default filters', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(SCRIPT_ERROR_EVENT, {})).toBe(null); expect(eventProcessor(TRANSACTION_EVENT, {})).toBe(TRANSACTION_EVENT); }); it('disable default error filters', () => { - const eventProcessor = createInboundFiltersEventProcessor({ disableErrorDefaults: true }); + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { disableErrorDefaults: true }); expect(eventProcessor(SCRIPT_ERROR_EVENT, {})).toBe(SCRIPT_ERROR_EVENT); }); }); describe('denyUrls/allowUrls', () => { it('should filter captured message based on its stack trace using string filter', () => { - const eventProcessorDeny = createInboundFiltersEventProcessor({ + const eventProcessorDeny = createEventFiltersEventProcessor(integrationFn, { denyUrls: ['https://awesome-analytics.io'], }); expect(eventProcessorDeny(MESSAGE_EVENT_WITH_STACKTRACE, {})).toBe(null); }); it('should allow denyUrls to take precedence', () => { - const eventProcessorBoth = createInboundFiltersEventProcessor({ + const eventProcessorBoth = createEventFiltersEventProcessor(integrationFn, { allowUrls: ['https://awesome-analytics.io'], denyUrls: ['https://awesome-analytics.io'], }); @@ -553,97 +673,127 @@ describe('InboundFilters', () => { }); it('should filter captured message based on its stack trace using regexp filter', () => { - const eventProcessorDeny = createInboundFiltersEventProcessor({ + const eventProcessorDeny = createEventFiltersEventProcessor(integrationFn, { denyUrls: [/awesome-analytics\.io/], }); expect(eventProcessorDeny(MESSAGE_EVENT_WITH_STACKTRACE, {})).toBe(null); }); it('should not filter captured messages with no stacktraces', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { denyUrls: ['https://awesome-analytics.io'], }); expect(eventProcessor(MESSAGE_EVENT, {})).toBe(MESSAGE_EVENT); }); it('should filter captured exception based on its stack trace using string filter', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { denyUrls: ['https://awesome-analytics.io'], }); expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES, {})).toBe(null); }); it('should filter captured exception based on its stack trace using regexp filter', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { denyUrls: [/awesome-analytics\.io/], }); expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES, {})).toBe(null); }); it("should not filter events that don't match the filtered values", () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { denyUrls: ['some-other-domain.com'], }); expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES, {})).toBe(EXCEPTION_EVENT_WITH_FRAMES); }); it('should be able to use multiple filters', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { denyUrls: ['some-other-domain.com', /awesome-analytics\.io/], }); expect(eventProcessor(EXCEPTION_EVENT_WITH_FRAMES, {})).toBe(null); }); it('should not fail with malformed event event', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { denyUrls: ['https://awesome-analytics.io'], }); expect(eventProcessor(MALFORMED_EVENT, {})).toBe(MALFORMED_EVENT); }); it('should search for script names when there is an anonymous callback at the last frame', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { denyUrls: ['https://awesome-analytics.io/some/file.js'], }); expect(eventProcessor(MESSAGE_EVENT_WITH_ANON_LAST_FRAME, {})).toBe(null); }); it('should search for script names when the last frame is from native code', () => { - const eventProcessor = createInboundFiltersEventProcessor({ + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { denyUrls: ['https://awesome-analytics.io/some/file.js'], }); expect(eventProcessor(MESSAGE_EVENT_WITH_NATIVE_LAST_FRAME, {})).toBe(null); }); + + it('should apply denyUrls to the "root" error of a linked exception', () => { + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { + denyUrls: ['https://main-error.com'], + }); + expect(eventProcessor(EXCEPTION_EVENT_WITH_LINKED_ERRORS, {})).toBe(null); + }); + + it('should apply denyUrls to the "root" error of an aggregate exception', () => { + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { + denyUrls: ['https://main-error.com'], + }); + expect(eventProcessor(EXCEPTION_EVENT_WITH_AGGREGATE_ERRORS, {})).toBe(null); + }); + + it('should apply allowUrls to the "most root" exception in the event if there are exceptions without stacktrace', () => { + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { + allowUrls: ['https://some-error-that-is-not-main-error.com'], + }); + expect(eventProcessor(EXCEPTION_EVENT_WITH_LINKED_ERRORS_WITHOUT_STACKTRACE, {})).toBe(null); + }); + + it('should not apply allowUrls to the event when the "root" exception of an aggregate error doesn\'t have a stacktrace', () => { + const eventProcessor = createEventFiltersEventProcessor(integrationFn, { + allowUrls: ['https://some-error-that-doesnt-match-anything.com'], + }); + expect(eventProcessor(EXCEPTION_EVENT_WITH_AGGREGATE_ERRORS_WITHOUT_STACKTRACE, {})).toBe( + EXCEPTION_EVENT_WITH_AGGREGATE_ERRORS_WITHOUT_STACKTRACE, + ); + }); }); describe('useless errors', () => { it("should drop event with exceptions that don't have any message, type or stack trace", () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(USELESS_EXCEPTION_EVENT, {})).toBe(null); }); it('should drop event with just a generic error without stacktrace or message', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(USELESS_ERROR_EXCEPTION_EVENT, {})).toBe(null); }); it('should not drop event with a message', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(EVENT_WITH_MESSAGE, {})).toBe(EVENT_WITH_MESSAGE); }); it('should not drop event with an exception that has a type', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(EVENT_WITH_TYPE, {})).toBe(EVENT_WITH_TYPE); }); it('should not drop event with an exception that has a stacktrace', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(EVENT_WITH_STACKTRACE, {})).toBe(EVENT_WITH_STACKTRACE); }); it('should not drop event with an exception that has a value', () => { - const eventProcessor = createInboundFiltersEventProcessor(); + const eventProcessor = createEventFiltersEventProcessor(integrationFn); expect(eventProcessor(EVENT_WITH_VALUE, {})).toBe(EVENT_WITH_VALUE); }); }); diff --git a/packages/core/test/lib/integrations/extraerrordata.test.ts b/packages/core/test/lib/integrations/extraerrordata.test.ts index c4048f3122b1..33f1f1debe8e 100644 --- a/packages/core/test/lib/integrations/extraerrordata.test.ts +++ b/packages/core/test/lib/integrations/extraerrordata.test.ts @@ -2,6 +2,7 @@ import type { Event as SentryEvent, ExtendedError } from '../../../src/types-hoi import { extraErrorDataIntegration } from '../../../src/integrations/extraerrordata'; +import { beforeEach, describe, expect, it } from 'vitest'; import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; const extraErrorData = extraErrorDataIntegration(); diff --git a/packages/core/test/lib/integrations/functiontostring.test.ts b/packages/core/test/lib/integrations/functiontostring.test.ts index 37c12e2c2b92..09d03c1893e8 100644 --- a/packages/core/test/lib/integrations/functiontostring.test.ts +++ b/packages/core/test/lib/integrations/functiontostring.test.ts @@ -1,3 +1,4 @@ +import { afterAll, beforeEach, describe, expect, it } from 'vitest'; import { fill, getClient, getCurrentScope, setCurrentClient } from '../../../src'; import { functionToStringIntegration } from '../../../src/integrations/functiontostring'; import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; diff --git a/packages/core/test/lib/integrations/metadata.test.ts b/packages/core/test/lib/integrations/metadata.test.ts index c93f930ca273..5f685843a640 100644 --- a/packages/core/test/lib/integrations/metadata.test.ts +++ b/packages/core/test/lib/integrations/metadata.test.ts @@ -1,5 +1,6 @@ import type { Event } from '../../../src/types-hoist'; +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { GLOBAL_OBJ, captureException, @@ -26,49 +27,50 @@ describe('ModuleMetadata integration', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); - test('Adds and removes metadata from stack frames', done => { - const options = getDefaultTestClientOptions({ - dsn: 'https://username@domain/123', - enableSend: true, - stackParser, - integrations: [moduleMetadataIntegration()], - beforeSend: (event, _hint) => { - // copy the frames since reverse in in-place - const lastFrame = [...(event.exception?.values?.[0]?.stacktrace?.frames || [])].reverse()[0]; - // Ensure module_metadata is populated in beforeSend callback - expect(lastFrame?.module_metadata).toEqual({ team: 'frontend' }); - return event; - }, - transport: () => - createTransport({ recordDroppedEvent: () => undefined }, async req => { - const [, items] = parseEnvelope(req.body); + test('Adds and removes metadata from stack frames', () => + new Promise(done => { + const options = getDefaultTestClientOptions({ + dsn: 'https://username@domain/123', + enableSend: true, + stackParser, + integrations: [moduleMetadataIntegration()], + beforeSend: (event, _hint) => { + // copy the frames since reverse in in-place + const lastFrame = [...(event.exception?.values?.[0]?.stacktrace?.frames || [])].reverse()[0]; + // Ensure module_metadata is populated in beforeSend callback + expect(lastFrame?.module_metadata).toEqual({ team: 'frontend' }); + return event; + }, + transport: () => + createTransport({ recordDroppedEvent: () => undefined }, async req => { + const [, items] = parseEnvelope(req.body); - expect(items[0][1]).toBeDefined(); - const event = items[0][1] as Event; - const error = event.exception?.values?.[0]; + expect(items[0][1]).toBeDefined(); + const event = items[0][1] as Event; + const error = event.exception?.values?.[0]; - // Ensure we're looking at the same error we threw - expect(error?.value).toEqual('Some error'); + // Ensure we're looking at the same error we threw + expect(error?.value).toEqual('Some error'); - const lastFrame = [...(error?.stacktrace?.frames || [])].reverse()[0]; - // Ensure the last frame is in fact for this file - expect(lastFrame?.filename).toEqual(__filename); + const lastFrame = [...(error?.stacktrace?.frames || [])].reverse()[0]; + // Ensure the last frame is in fact for this file + expect(lastFrame?.filename).toEqual(__filename); - // Ensure module_metadata has been stripped from the event - expect(lastFrame?.module_metadata).toBeUndefined(); + // Ensure module_metadata has been stripped from the event + expect(lastFrame?.module_metadata).toBeUndefined(); - done(); - return {}; - }), - }); + done(); + return {}; + }), + }); - const client = new TestClient(options); - setCurrentClient(client); - client.init(); + const client = new TestClient(options); + setCurrentClient(client); + client.init(); - captureException(new Error('Some error')); - }); + captureException(new Error('Some error')); + })); }); diff --git a/packages/core/test/lib/integrations/rewriteframes.test.ts b/packages/core/test/lib/integrations/rewriteframes.test.ts index e31976ba5785..250f2bac4dc8 100644 --- a/packages/core/test/lib/integrations/rewriteframes.test.ts +++ b/packages/core/test/lib/integrations/rewriteframes.test.ts @@ -1,5 +1,6 @@ import type { Event, StackFrame } from '../../../src/types-hoist'; +import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { generateIteratee, rewriteFramesIntegration } from '../../../src/integrations/rewriteframes'; let rewriteFrames: ReturnType; diff --git a/packages/core/test/lib/integrations/third-party-errors-filter.test.ts b/packages/core/test/lib/integrations/third-party-errors-filter.test.ts index 9679cfe2b474..dd5dab174fce 100644 --- a/packages/core/test/lib/integrations/third-party-errors-filter.test.ts +++ b/packages/core/test/lib/integrations/third-party-errors-filter.test.ts @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it } from 'vitest'; import type { Client } from '../../../src/client'; import { thirdPartyErrorFilterIntegration } from '../../../src/integrations/third-party-errors-filter'; import { addMetadataToStackFrames } from '../../../src/metadata'; diff --git a/packages/core/test/lib/integrations/zoderrrors.test.ts b/packages/core/test/lib/integrations/zoderrrors.test.ts index cd80e2347f36..b29bb09845e8 100644 --- a/packages/core/test/lib/integrations/zoderrrors.test.ts +++ b/packages/core/test/lib/integrations/zoderrrors.test.ts @@ -1,6 +1,7 @@ import { z } from 'zod'; import type { Event, EventHint } from '../../../src/types-hoist'; +import { describe, expect, it, test } from 'vitest'; import { applyZodErrorsToEvent, flattenIssue, @@ -168,27 +169,27 @@ describe('applyZodErrorsToEvent()', () => { } expect(attachment.filename).toBe('zod_issues.json'); expect(JSON.parse(attachment.data.toString())).toMatchInlineSnapshot(` -Object { - "issues": Array [ - Object { - "code": "invalid_type", - "expected": "string", - "keys": "[\\"extra\\"]", - "message": "Invalid input: expected string, received number", - "path": "names.1", - "received": "number", - }, - Object { - "code": "invalid_type", - "expected": "string", - "keys": "[\\"extra2\\"]", - "message": "Invalid input: expected string, received number", - "path": "foo.1", - "received": "number", - }, - ], -} -`); + { + "issues": [ + { + "code": "invalid_type", + "expected": "string", + "keys": "["extra"]", + "message": "Invalid input: expected string, received number", + "path": "names.1", + "received": "number", + }, + { + "code": "invalid_type", + "expected": "string", + "keys": "["extra2"]", + "message": "Invalid input: expected string, received number", + "path": "foo.1", + "received": "number", + }, + ], + } + `); }); }); @@ -213,91 +214,91 @@ describe('flattenIssue()', () => { // Original zod error expect(zodError.issues).toMatchInlineSnapshot(` -Array [ - Object { - "code": "too_small", - "exact": false, - "inclusive": true, - "message": "String must contain at least 1 character(s)", - "minimum": 1, - "path": Array [ - "foo", - ], - "type": "string", - }, - Object { - "code": "invalid_literal", - "expected": "baz", - "message": "Invalid literal value, expected \\"baz\\"", - "path": Array [ - "nested", - "bar", - ], - "received": "not-baz", - }, -] -`); + [ + { + "code": "too_small", + "exact": false, + "inclusive": true, + "message": "String must contain at least 1 character(s)", + "minimum": 1, + "path": [ + "foo", + ], + "type": "string", + }, + { + "code": "invalid_literal", + "expected": "baz", + "message": "Invalid literal value, expected "baz"", + "path": [ + "nested", + "bar", + ], + "received": "not-baz", + }, + ] + `); const issues = zodError.issues; expect(issues.length).toBe(2); // Format it for use in Sentry expect(issues.map(flattenIssue)).toMatchInlineSnapshot(` -Array [ - Object { - "code": "too_small", - "exact": false, - "inclusive": true, - "keys": undefined, - "message": "String must contain at least 1 character(s)", - "minimum": 1, - "path": "foo", - "type": "string", - "unionErrors": undefined, - }, - Object { - "code": "invalid_literal", - "expected": "baz", - "keys": undefined, - "message": "Invalid literal value, expected \\"baz\\"", - "path": "nested.bar", - "received": "not-baz", - "unionErrors": undefined, - }, -] -`); + [ + { + "code": "too_small", + "exact": false, + "inclusive": true, + "keys": undefined, + "message": "String must contain at least 1 character(s)", + "minimum": 1, + "path": "foo", + "type": "string", + "unionErrors": undefined, + }, + { + "code": "invalid_literal", + "expected": "baz", + "keys": undefined, + "message": "Invalid literal value, expected "baz"", + "path": "nested.bar", + "received": "not-baz", + "unionErrors": undefined, + }, + ] + `); expect(zodError.flatten(flattenIssue)).toMatchInlineSnapshot(` -Object { - "fieldErrors": Object { - "foo": Array [ - Object { - "code": "too_small", - "exact": false, - "inclusive": true, - "keys": undefined, - "message": "String must contain at least 1 character(s)", - "minimum": 1, - "path": "foo", - "type": "string", - "unionErrors": undefined, - }, - ], - "nested": Array [ - Object { - "code": "invalid_literal", - "expected": "baz", - "keys": undefined, - "message": "Invalid literal value, expected \\"baz\\"", - "path": "nested.bar", - "received": "not-baz", - "unionErrors": undefined, - }, - ], - }, - "formErrors": Array [], -} -`); + { + "fieldErrors": { + "foo": [ + { + "code": "too_small", + "exact": false, + "inclusive": true, + "keys": undefined, + "message": "String must contain at least 1 character(s)", + "minimum": 1, + "path": "foo", + "type": "string", + "unionErrors": undefined, + }, + ], + "nested": [ + { + "code": "invalid_literal", + "expected": "baz", + "keys": undefined, + "message": "Invalid literal value, expected "baz"", + "path": "nested.bar", + "received": "not-baz", + "unionErrors": undefined, + }, + ], + }, + "formErrors": [], + } + `); }); it('flattens keys field to string', () => { @@ -317,18 +318,18 @@ Object { // Original zod error expect(zodError.issues).toMatchInlineSnapshot(` -Array [ - Object { - "code": "unrecognized_keys", - "keys": Array [ - "extra_key_abc", - "extra_key_def", - ], - "message": "Unrecognized key(s) in object: 'extra_key_abc', 'extra_key_def'", - "path": Array [], - }, -] -`); + [ + { + "code": "unrecognized_keys", + "keys": [ + "extra_key_abc", + "extra_key_def", + ], + "message": "Unrecognized key(s) in object: 'extra_key_abc', 'extra_key_def'", + "path": [], + }, + ] + `); const issues = zodError.issues; expect(issues.length).toBe(1); @@ -344,14 +345,14 @@ Array [ // Note: path is an empty string because the issue is at the root. // TODO: Maybe somehow make it clearer that this is at the root? expect(formattedIssue).toMatchInlineSnapshot(` -Object { - "code": "unrecognized_keys", - "keys": "[\\"extra_key_abc\\",\\"extra_key_def\\"]", - "message": "Unrecognized key(s) in object: 'extra_key_abc', 'extra_key_def'", - "path": "", - "unionErrors": undefined, -} -`); + { + "code": "unrecognized_keys", + "keys": "["extra_key_abc","extra_key_def"]", + "message": "Unrecognized key(s) in object: 'extra_key_abc', 'extra_key_def'", + "path": "", + "unionErrors": undefined, + } + `); expect(typeof formattedIssue.keys === 'string').toBe(true); }); }); @@ -406,16 +407,16 @@ describe('formatIssueMessage()', () => { // Original zod error expect(zodError.issues).toMatchInlineSnapshot(` -Array [ - Object { - "code": "invalid_type", - "expected": "object", - "message": "Expected object, received number", - "path": Array [], - "received": "number", - }, -] -`); + [ + { + "code": "invalid_type", + "expected": "object", + "message": "Expected object, received number", + "path": [], + "received": "number", + }, + ] + `); const message = formatIssueMessage(zodError); expect(message).toMatchInlineSnapshot('"Failed to validate object"'); @@ -429,16 +430,16 @@ Array [ // Original zod error expect(zodError.issues).toMatchInlineSnapshot(` -Array [ - Object { - "code": "invalid_type", - "expected": "number", - "message": "Expected number, received string", - "path": Array [], - "received": "string", - }, -] -`); + [ + { + "code": "invalid_type", + "expected": "number", + "message": "Expected number, received string", + "path": [], + "received": "string", + }, + ] + `); const message = formatIssueMessage(zodError); expect(message).toMatchInlineSnapshot('"Failed to validate number"'); @@ -452,16 +453,16 @@ Array [ // Original zod error expect(zodError.issues).toMatchInlineSnapshot(` -Array [ - Object { - "code": "invalid_type", - "expected": "string", - "message": "Expected string, received number", - "path": Array [], - "received": "number", - }, -] -`); + [ + { + "code": "invalid_type", + "expected": "string", + "message": "Expected string, received number", + "path": [], + "received": "number", + }, + ] + `); const message = formatIssueMessage(zodError); expect(message).toMatchInlineSnapshot('"Failed to validate string"'); @@ -475,16 +476,16 @@ Array [ // Original zod error expect(zodError.issues).toMatchInlineSnapshot(` -Array [ - Object { - "code": "invalid_type", - "expected": "array", - "message": "Expected array, received string", - "path": Array [], - "received": "string", - }, -] -`); + [ + { + "code": "invalid_type", + "expected": "array", + "message": "Expected array, received string", + "path": [], + "received": "string", + }, + ] + `); const message = formatIssueMessage(zodError); expect(message).toMatchInlineSnapshot('"Failed to validate array"'); @@ -498,18 +499,18 @@ Array [ // Original zod error expect(zodError.issues).toMatchInlineSnapshot(` -Array [ - Object { - "code": "invalid_type", - "expected": "string", - "message": "Expected string, received number", - "path": Array [ - 0, - ], - "received": "number", - }, -] -`); + [ + { + "code": "invalid_type", + "expected": "string", + "message": "Expected string, received number", + "path": [ + 0, + ], + "received": "number", + }, + ] + `); const message = formatIssueMessage(zodError); expect(message).toMatchInlineSnapshot('"Failed to validate keys: "'); diff --git a/packages/core/test/lib/metadata.test.ts b/packages/core/test/lib/metadata.test.ts index 7dbbe8a0ea1b..7e402bbee0b1 100644 --- a/packages/core/test/lib/metadata.test.ts +++ b/packages/core/test/lib/metadata.test.ts @@ -1,5 +1,6 @@ import type { Event } from '../../src/types-hoist'; +import { beforeEach, describe, expect, it } from 'vitest'; import { addMetadataToStackFrames, getMetadataForUrl, stripMetadataFromStackFrames } from '../../src/metadata'; import { nodeStackLineParser } from '../../src/utils-hoist/node-stack-trace'; import { createStackParser } from '../../src/utils-hoist/stacktrace'; diff --git a/packages/core/test/lib/prepareEvent.test.ts b/packages/core/test/lib/prepareEvent.test.ts index 9806bd06bd14..2e7f5880413d 100644 --- a/packages/core/test/lib/prepareEvent.test.ts +++ b/packages/core/test/lib/prepareEvent.test.ts @@ -2,6 +2,7 @@ import type { Client, ScopeContext } from '../../src'; import { GLOBAL_OBJ, createStackParser, getGlobalScope, getIsolationScope } from '../../src'; import type { Attachment, Breadcrumb, ClientOptions, Event, EventHint, EventProcessor } from '../../src/types-hoist'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { Scope } from '../../src/scope'; import { applyClientOptions, @@ -270,7 +271,7 @@ describe('prepareEvent', () => { }); it('works without any scope data', async () => { - const eventProcessor = jest.fn((a: unknown) => a) as EventProcessor; + const eventProcessor = vi.fn((a: unknown) => a) as EventProcessor; const scope = new Scope(); @@ -315,9 +316,9 @@ describe('prepareEvent', () => { const breadcrumb3 = { message: '3', timestamp: 123 } as Breadcrumb; const breadcrumb4 = { message: '4', timestamp: 123 } as Breadcrumb; - const eventProcessor1 = jest.fn((a: unknown) => a) as EventProcessor; - const eventProcessor2 = jest.fn((b: unknown) => b) as EventProcessor; - const eventProcessor3 = jest.fn((b: unknown) => b) as EventProcessor; + const eventProcessor1 = vi.fn((a: unknown) => a) as EventProcessor; + const eventProcessor2 = vi.fn((b: unknown) => b) as EventProcessor; + const eventProcessor3 = vi.fn((b: unknown) => b) as EventProcessor; const attachment1 = { filename: '1' } as Attachment; const attachment2 = { filename: '2' } as Attachment; @@ -399,8 +400,8 @@ describe('prepareEvent', () => { const breadcrumb2 = { message: '2', timestamp: 222 } as Breadcrumb; const breadcrumb3 = { message: '3', timestamp: 333 } as Breadcrumb; - const eventProcessor1 = jest.fn((a: unknown) => a) as EventProcessor; - const eventProcessor2 = jest.fn((a: unknown) => a) as EventProcessor; + const eventProcessor1 = vi.fn((a: unknown) => a) as EventProcessor; + const eventProcessor2 = vi.fn((a: unknown) => a) as EventProcessor; const attachmentGlobal = { filename: 'global scope attachment' } as Attachment; const attachmentIsolation = { filename: 'isolation scope attachment' } as Attachment; @@ -563,7 +564,7 @@ describe('prepareEvent', () => { const captureContextScope = new Scope(); captureContextScope.setTags({ foo: 'bar' }); - const captureContext = jest.fn(passedScope => { + const captureContext = vi.fn(passedScope => { expect(passedScope).toEqual(scope); return captureContextScope; }); diff --git a/packages/core/test/lib/scope.test.ts b/packages/core/test/lib/scope.test.ts index 026d33fd54e5..4b27eff42824 100644 --- a/packages/core/test/lib/scope.test.ts +++ b/packages/core/test/lib/scope.test.ts @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it, test, vi } from 'vitest'; import type { Client } from '../../src'; import { applyScopeDataToEvent, @@ -338,7 +339,7 @@ describe('Scope', () => { }); test('given callback function, pass it the scope and returns original or modified scope', () => { - const cb = jest + const cb = vi .fn() .mockImplementationOnce(v => v) .mockImplementationOnce(v => { @@ -356,7 +357,7 @@ describe('Scope', () => { }); test('given callback function, when it doesnt return instanceof Scope, ignore it and return original scope', () => { - const cb = jest.fn().mockImplementationOnce(_v => 'wat'); + const cb = vi.fn().mockImplementationOnce(_v => 'wat'); const updatedScope = scope.update(cb); expect(cb).toHaveBeenCalledWith(scope); expect(updatedScope).toEqual(scope); @@ -557,7 +558,7 @@ describe('Scope', () => { describe('.captureException()', () => { it('should call captureException() on client with newly generated event ID if not explicitly passed in', () => { - const fakeCaptureException = jest.fn(() => 'mock-event-id'); + const fakeCaptureException = vi.fn(() => 'mock-event-id'); const fakeClient = { captureException: fakeCaptureException, } as unknown as Client; @@ -586,7 +587,7 @@ describe('Scope', () => { }); it('should pass exception to captureException() on client', () => { - const fakeCaptureException = jest.fn(() => 'mock-event-id'); + const fakeCaptureException = vi.fn(() => 'mock-event-id'); const fakeClient = { captureException: fakeCaptureException, } as unknown as Client; @@ -601,7 +602,7 @@ describe('Scope', () => { }); it('should call captureException() on client with a synthetic exception', () => { - const fakeCaptureException = jest.fn(() => 'mock-event-id'); + const fakeCaptureException = vi.fn(() => 'mock-event-id'); const fakeClient = { captureException: fakeCaptureException, } as unknown as Client; @@ -618,7 +619,7 @@ describe('Scope', () => { }); it('should pass the original exception to captureException() on client', () => { - const fakeCaptureException = jest.fn(() => 'mock-event-id'); + const fakeCaptureException = vi.fn(() => 'mock-event-id'); const fakeClient = { captureException: fakeCaptureException, } as unknown as Client; @@ -636,7 +637,7 @@ describe('Scope', () => { }); it('should forward hint to captureException() on client', () => { - const fakeCaptureException = jest.fn(() => 'mock-event-id'); + const fakeCaptureException = vi.fn(() => 'mock-event-id'); const fakeClient = { captureException: fakeCaptureException, } as unknown as Client; @@ -655,7 +656,7 @@ describe('Scope', () => { describe('.captureMessage()', () => { it('should call captureMessage() on client with newly generated event ID if not explicitly passed in', () => { - const fakeCaptureMessage = jest.fn(() => 'mock-event-id'); + const fakeCaptureMessage = vi.fn(() => 'mock-event-id'); const fakeClient = { captureMessage: fakeCaptureMessage, } as unknown as Client; @@ -681,7 +682,7 @@ describe('Scope', () => { }); it('should pass exception to captureMessage() on client', () => { - const fakeCaptureMessage = jest.fn(() => 'mock-event-id'); + const fakeCaptureMessage = vi.fn(() => 'mock-event-id'); const fakeClient = { captureMessage: fakeCaptureMessage, } as unknown as Client; @@ -694,7 +695,7 @@ describe('Scope', () => { }); it('should call captureMessage() on client with a synthetic exception', () => { - const fakeCaptureMessage = jest.fn(() => 'mock-event-id'); + const fakeCaptureMessage = vi.fn(() => 'mock-event-id'); const fakeClient = { captureMessage: fakeCaptureMessage, } as unknown as Client; @@ -712,7 +713,7 @@ describe('Scope', () => { }); it('should pass the original exception to captureMessage() on client', () => { - const fakeCaptureMessage = jest.fn(() => 'mock-event-id'); + const fakeCaptureMessage = vi.fn(() => 'mock-event-id'); const fakeClient = { captureMessage: fakeCaptureMessage, } as unknown as Client; @@ -730,7 +731,7 @@ describe('Scope', () => { }); it('should forward level and hint to captureMessage() on client', () => { - const fakeCaptureMessage = jest.fn(() => 'mock-event-id'); + const fakeCaptureMessage = vi.fn(() => 'mock-event-id'); const fakeClient = { captureMessage: fakeCaptureMessage, } as unknown as Client; @@ -750,7 +751,7 @@ describe('Scope', () => { describe('.captureEvent()', () => { it('should call captureEvent() on client with newly generated event ID if not explicitly passed in', () => { - const fakeCaptureEvent = jest.fn(() => 'mock-event-id'); + const fakeCaptureEvent = vi.fn(() => 'mock-event-id'); const fakeClient = { captureEvent: fakeCaptureEvent, } as unknown as Client; @@ -775,7 +776,7 @@ describe('Scope', () => { }); it('should pass event to captureEvent() on client', () => { - const fakeCaptureEvent = jest.fn(() => 'mock-event-id'); + const fakeCaptureEvent = vi.fn(() => 'mock-event-id'); const fakeClient = { captureEvent: fakeCaptureEvent, } as unknown as Client; @@ -790,7 +791,7 @@ describe('Scope', () => { }); it('should forward hint to captureEvent() on client', () => { - const fakeCaptureEvent = jest.fn(() => 'mock-event-id'); + const fakeCaptureEvent = vi.fn(() => 'mock-event-id'); const fakeClient = { captureEvent: fakeCaptureEvent, } as unknown as Client; @@ -847,67 +848,73 @@ describe('withScope()', () => { getGlobalScope().clear(); }); - it('will make the passed scope the active scope within the callback', done => { - withScope(scope => { - expect(getCurrentScope()).toBe(scope); - done(); - }); - }); - - it('will pass a scope that is different from the current active isolation scope', done => { - withScope(scope => { - expect(getIsolationScope()).not.toBe(scope); - done(); - }); - }); - - it('will always make the inner most passed scope the current isolation scope when nesting calls', done => { - withIsolationScope(_scope1 => { - withIsolationScope(scope2 => { - expect(getIsolationScope()).toBe(scope2); + it('will make the passed scope the active scope within the callback', () => + new Promise(done => { + withScope(scope => { + expect(getCurrentScope()).toBe(scope); done(); }); - }); - }); - - it('forks the scope when not passing any scope', done => { - const initialScope = getCurrentScope(); - initialScope.setTag('aa', 'aa'); - - withScope(scope => { - expect(getCurrentScope()).toBe(scope); - scope.setTag('bb', 'bb'); - expect(scope).not.toBe(initialScope); - expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); - done(); - }); - }); - - it('forks the scope when passing undefined', done => { - const initialScope = getCurrentScope(); - initialScope.setTag('aa', 'aa'); + })); - withScope(undefined, scope => { - expect(getCurrentScope()).toBe(scope); - scope.setTag('bb', 'bb'); - expect(scope).not.toBe(initialScope); - expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); - done(); - }); - }); + it('will pass a scope that is different from the current active isolation scope', () => + new Promise(done => { + withScope(scope => { + expect(getIsolationScope()).not.toBe(scope); + done(); + }); + })); + + it('will always make the inner most passed scope the current isolation scope when nesting calls', () => + new Promise(done => { + withIsolationScope(_scope1 => { + withIsolationScope(scope2 => { + expect(getIsolationScope()).toBe(scope2); + done(); + }); + }); + })); + + it('forks the scope when not passing any scope', () => + new Promise(done => { + const initialScope = getCurrentScope(); + initialScope.setTag('aa', 'aa'); + + withScope(scope => { + expect(getCurrentScope()).toBe(scope); + scope.setTag('bb', 'bb'); + expect(scope).not.toBe(initialScope); + expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); + done(); + }); + })); + + it('forks the scope when passing undefined', () => + new Promise(done => { + const initialScope = getCurrentScope(); + initialScope.setTag('aa', 'aa'); + + withScope(undefined, scope => { + expect(getCurrentScope()).toBe(scope); + scope.setTag('bb', 'bb'); + expect(scope).not.toBe(initialScope); + expect(scope.getScopeData().tags).toEqual({ aa: 'aa', bb: 'bb' }); + done(); + }); + })); - it('sets the passed in scope as active scope', done => { - const initialScope = getCurrentScope(); - initialScope.setTag('aa', 'aa'); + it('sets the passed in scope as active scope', () => + new Promise(done => { + const initialScope = getCurrentScope(); + initialScope.setTag('aa', 'aa'); - const customScope = new Scope(); + const customScope = new Scope(); - withScope(customScope, scope => { - expect(getCurrentScope()).toBe(customScope); - expect(scope).toBe(customScope); - done(); - }); - }); + withScope(customScope, scope => { + expect(getCurrentScope()).toBe(customScope); + expect(scope).toBe(customScope); + done(); + }); + })); }); describe('withIsolationScope()', () => { @@ -917,58 +924,64 @@ describe('withIsolationScope()', () => { getGlobalScope().clear(); }); - it('will make the passed isolation scope the active isolation scope within the callback', done => { - withIsolationScope(scope => { - expect(getIsolationScope()).toBe(scope); - done(); - }); - }); - - it('will pass an isolation scope that is different from the current active scope', done => { - withIsolationScope(scope => { - expect(getCurrentScope()).not.toBe(scope); - done(); - }); - }); + it('will make the passed isolation scope the active isolation scope within the callback', () => + new Promise(done => { + withIsolationScope(scope => { + expect(getIsolationScope()).toBe(scope); + done(); + }); + })); - it('will always make the inner most passed scope the current scope when nesting calls', done => { - withIsolationScope(_scope1 => { - withIsolationScope(scope2 => { - expect(getIsolationScope()).toBe(scope2); + it('will pass an isolation scope that is different from the current active scope', () => + new Promise(done => { + withIsolationScope(scope => { + expect(getCurrentScope()).not.toBe(scope); done(); }); - }); - }); + })); + + it('will always make the inner most passed scope the current scope when nesting calls', () => + new Promise(done => { + withIsolationScope(_scope1 => { + withIsolationScope(scope2 => { + expect(getIsolationScope()).toBe(scope2); + done(); + }); + }); + })); // Note: This is expected! In browser, we do not actually fork this - it('does not fork isolation scope when not passing any isolation scope', done => { - const isolationScope = getIsolationScope(); + it('does not fork isolation scope when not passing any isolation scope', () => + new Promise(done => { + const isolationScope = getIsolationScope(); - withIsolationScope(scope => { - expect(getIsolationScope()).toBe(scope); - expect(scope).toBe(isolationScope); - done(); - }); - }); + withIsolationScope(scope => { + expect(getIsolationScope()).toBe(scope); + expect(scope).toBe(isolationScope); + done(); + }); + })); - it('does not fork isolation scope when passing undefined', done => { - const isolationScope = getIsolationScope(); + it('does not fork isolation scope when passing undefined', () => + new Promise(done => { + const isolationScope = getIsolationScope(); - withIsolationScope(undefined, scope => { - expect(getIsolationScope()).toBe(scope); - expect(scope).toBe(isolationScope); - done(); - }); - }); + withIsolationScope(undefined, scope => { + expect(getIsolationScope()).toBe(scope); + expect(scope).toBe(isolationScope); + done(); + }); + })); - it('ignores passed in isolation scope', done => { - const isolationScope = getIsolationScope(); - const customIsolationScope = new Scope(); + it('ignores passed in isolation scope', () => + new Promise(done => { + const isolationScope = getIsolationScope(); + const customIsolationScope = new Scope(); - withIsolationScope(customIsolationScope, scope => { - expect(getIsolationScope()).toBe(isolationScope); - expect(scope).toBe(isolationScope); - done(); - }); - }); + withIsolationScope(customIsolationScope, scope => { + expect(getIsolationScope()).toBe(isolationScope); + expect(scope).toBe(isolationScope); + done(); + }); + })); }); diff --git a/packages/core/test/lib/sdk.test.ts b/packages/core/test/lib/sdk.test.ts index 3cce0b5a9020..03404ce911f6 100644 --- a/packages/core/test/lib/sdk.test.ts +++ b/packages/core/test/lib/sdk.test.ts @@ -1,3 +1,4 @@ +import { type Mock, beforeEach, describe, expect, it, test, vi } from 'vitest'; import type { Client } from '../../src'; import { captureCheckIn, getCurrentScope, setCurrentClient } from '../../src'; import { installedIntegrations } from '../../src/integration'; @@ -12,7 +13,7 @@ const PUBLIC_DSN = 'https://username@domain/123'; export class MockIntegration implements Integration { public name: string; - public setupOnce: () => void = jest.fn(); + public setupOnce: () => void = vi.fn(); public constructor(name: string) { this.name = name; } @@ -32,8 +33,8 @@ describe('SDK', () => { ]; const options = getDefaultTestClientOptions({ dsn: PUBLIC_DSN, integrations }); initAndBind(TestClient, options); - expect((integrations[0]?.setupOnce as jest.Mock).mock.calls.length).toBe(1); - expect((integrations[1]?.setupOnce as jest.Mock).mock.calls.length).toBe(1); + expect((integrations[0]?.setupOnce as Mock).mock.calls.length).toBe(1); + expect((integrations[1]?.setupOnce as Mock).mock.calls.length).toBe(1); }); test('calls hooks in the correct order', () => { @@ -41,21 +42,21 @@ describe('SDK', () => { const integration1 = { name: 'integration1', - setupOnce: jest.fn(() => list.push('setupOnce1')), - afterAllSetup: jest.fn(() => list.push('afterAllSetup1')), + setupOnce: vi.fn(() => list.push('setupOnce1')), + afterAllSetup: vi.fn(() => list.push('afterAllSetup1')), } satisfies Integration; const integration2 = { name: 'integration2', - setupOnce: jest.fn(() => list.push('setupOnce2')), - setup: jest.fn(() => list.push('setup2')), - afterAllSetup: jest.fn(() => list.push('afterAllSetup2')), + setupOnce: vi.fn(() => list.push('setupOnce2')), + setup: vi.fn(() => list.push('setup2')), + afterAllSetup: vi.fn(() => list.push('afterAllSetup2')), } satisfies Integration; const integration3 = { name: 'integration3', - setupOnce: jest.fn(() => list.push('setupOnce3')), - setup: jest.fn(() => list.push('setup3')), + setupOnce: vi.fn(() => list.push('setupOnce3')), + setup: vi.fn(() => list.push('setup3')), } satisfies Integration; const integrations: Integration[] = [integration1, integration2, integration3]; diff --git a/packages/core/test/lib/server-runtime-client.test.ts b/packages/core/test/lib/server-runtime-client.test.ts index 2eeb90083f29..bbe1e441cad4 100644 --- a/packages/core/test/lib/server-runtime-client.test.ts +++ b/packages/core/test/lib/server-runtime-client.test.ts @@ -1,5 +1,6 @@ import type { Event, EventHint } from '../../src/types-hoist'; +import { describe, expect, it, test, vi } from 'vitest'; import { Scope, createTransport } from '../../src'; import type { ServerRuntimeClientOptions } from '../../src/server-runtime-client'; import { ServerRuntimeClient } from '../../src/server-runtime-client'; @@ -80,7 +81,7 @@ describe('ServerRuntimeClient', () => { }); client = new ServerRuntimeClient(options); - const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope'); + const sendEnvelopeSpy = vi.spyOn(client, 'sendEnvelope'); const id = client.captureCheckIn( { monitorSlug: 'foo', status: 'in_progress' }, @@ -150,7 +151,7 @@ describe('ServerRuntimeClient', () => { const options = getDefaultClientOptions({ dsn: PUBLIC_DSN, serverName: 'bar', enabled: false }); client = new ServerRuntimeClient(options); - const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope'); + const sendEnvelopeSpy = vi.spyOn(client, 'sendEnvelope'); client.captureCheckIn({ monitorSlug: 'foo', status: 'in_progress' }); @@ -163,7 +164,7 @@ describe('ServerRuntimeClient', () => { const options = getDefaultClientOptions({ dsn: PUBLIC_DSN }); client = new ServerRuntimeClient(options); - const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope'); + const sendEnvelopeSpy = vi.spyOn(client, 'sendEnvelope'); client.captureException(new Error('foo')); @@ -187,7 +188,7 @@ describe('ServerRuntimeClient', () => { const options = getDefaultClientOptions({ dsn: PUBLIC_DSN }); client = new ServerRuntimeClient(options); - const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope'); + const sendEnvelopeSpy = vi.spyOn(client, 'sendEnvelope'); client.captureMessage('foo'); diff --git a/packages/core/test/lib/session.test.ts b/packages/core/test/lib/session.test.ts index dfe96e135088..4864ea4623a1 100644 --- a/packages/core/test/lib/session.test.ts +++ b/packages/core/test/lib/session.test.ts @@ -1,5 +1,6 @@ import type { SessionContext } from '../../src/types-hoist'; +import { describe, expect, it, test } from 'vitest'; import { closeSession, makeSession, updateSession } from '../../src/session'; import { timestampInSeconds } from '../../src/utils-hoist/time'; diff --git a/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts b/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts index 63a6910de06b..23406908037c 100644 --- a/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts +++ b/packages/core/test/lib/tracing/dynamicSamplingContext.test.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'; import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, @@ -18,7 +19,7 @@ describe('getDynamicSamplingContextFromSpan', () => { }); afterEach(() => { - jest.resetAllMocks(); + vi.resetAllMocks(); }); test('uses frozen DSC from span', () => { diff --git a/packages/core/test/lib/tracing/errors.test.ts b/packages/core/test/lib/tracing/errors.test.ts index 43c6e8eca0ec..46717cae2459 100644 --- a/packages/core/test/lib/tracing/errors.test.ts +++ b/packages/core/test/lib/tracing/errors.test.ts @@ -1,6 +1,7 @@ import { setCurrentClient, spanToJSON, startInactiveSpan, startSpan } from '../../../src'; import type { HandlerDataError, HandlerDataUnhandledRejection } from '../../../src/types-hoist'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { _resetErrorsInstrumented, registerSpanErrorInstrumentation } from '../../../src/tracing/errors'; import * as globalErrorModule from '../../../src/utils-hoist/instrument/globalError'; import * as globalUnhandledRejectionModule from '../../../src/utils-hoist/instrument/globalUnhandledRejection'; @@ -9,12 +10,12 @@ import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; let mockErrorCallback: (data: HandlerDataError) => void = () => {}; let mockUnhandledRejectionCallback: (data: HandlerDataUnhandledRejection) => void = () => {}; -const mockAddGlobalErrorInstrumentationHandler = jest +const mockAddGlobalErrorInstrumentationHandler = vi .spyOn(globalErrorModule, 'addGlobalErrorInstrumentationHandler') .mockImplementation(callback => { mockErrorCallback = callback; }); -const mockAddGlobalUnhandledRejectionInstrumentationHandler = jest +const mockAddGlobalUnhandledRejectionInstrumentationHandler = vi .spyOn(globalUnhandledRejectionModule, 'addGlobalUnhandledRejectionInstrumentationHandler') .mockImplementation(callback => { mockUnhandledRejectionCallback = callback; diff --git a/packages/core/test/lib/tracing/idleSpan.test.ts b/packages/core/test/lib/tracing/idleSpan.test.ts index 5280be561067..35f9d59063fe 100644 --- a/packages/core/test/lib/tracing/idleSpan.test.ts +++ b/packages/core/test/lib/tracing/idleSpan.test.ts @@ -1,5 +1,6 @@ import { TestClient, getDefaultTestClientOptions } from '../../mocks/client'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON, SentryNonRecordingSpan, @@ -23,7 +24,7 @@ const dsn = 'https://123@sentry.io/42'; describe('startIdleSpan', () => { beforeEach(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); getCurrentScope().clear(); getIsolationScope().clear(); @@ -36,7 +37,7 @@ describe('startIdleSpan', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('sets & unsets the idle span on the scope', () => { @@ -47,7 +48,7 @@ describe('startIdleSpan', () => { expect(getActiveSpan()).toBe(idleSpan); idleSpan!.end(); - jest.runAllTimers(); + vi.runAllTimers(); expect(getActiveSpan()).toBe(undefined); }); @@ -82,13 +83,13 @@ describe('startIdleSpan', () => { const childSpan = startInactiveSpan({ name: 'inner2' }); span.end(); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout + 1); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout + 1); // Idle span is still recording expect(idleSpan.isRecording()).toBe(true); childSpan.end(); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout + 1); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout + 1); // Now it is finished! expect(idleSpan.isRecording()).toBe(false); @@ -96,7 +97,7 @@ describe('startIdleSpan', () => { }); it('calls beforeSpanEnd callback before finishing', () => { - const beforeSpanEnd = jest.fn(); + const beforeSpanEnd = vi.fn(); const idleSpan = startIdleSpan({ name: 'foo' }, { beforeSpanEnd }); expect(idleSpan).toBeDefined(); @@ -104,14 +105,14 @@ describe('startIdleSpan', () => { startSpan({ name: 'inner' }, () => {}); - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(beforeSpanEnd).toHaveBeenCalledTimes(1); expect(beforeSpanEnd).toHaveBeenLastCalledWith(idleSpan); }); it('allows to mutate idle span in beforeSpanEnd before it is sent', () => { const transactions: Event[] = []; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transactions.push(event); return null; }); @@ -127,7 +128,7 @@ describe('startIdleSpan', () => { // We want to accommodate a bit of drift there, so we ensure this starts earlier... const baseTimeInSeconds = Math.floor(Date.now() / 1000) - 9999; - const beforeSpanEnd = jest.fn((span: Span) => { + const beforeSpanEnd = vi.fn((span: Span) => { span.setAttribute('foo', 'bar'); // Try adding a child here - we do this in browser tracing... const inner = startInactiveSpan({ name: 'from beforeSpanEnd', startTime: baseTimeInSeconds }); @@ -138,8 +139,8 @@ describe('startIdleSpan', () => { expect(beforeSpanEnd).not.toHaveBeenCalled(); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout + 1); - jest.runOnlyPendingTimers(); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout + 1); + vi.runOnlyPendingTimers(); expect(spanToJSON(idleSpan!).data).toEqual( expect.objectContaining({ @@ -165,7 +166,7 @@ describe('startIdleSpan', () => { it('filters spans on end', () => { const transactions: Event[] = []; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transactions.push(event); return null; }); @@ -205,8 +206,8 @@ describe('startIdleSpan', () => { regularSpan.end(baseTimeInSeconds + 4); idleSpan.end(baseTimeInSeconds + 10); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout + 1); - jest.runOnlyPendingTimers(); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout + 1); + vi.runOnlyPendingTimers(); expect(regularSpan.isRecording()).toBe(false); expect(idleSpan.isRecording()).toBe(false); @@ -245,7 +246,7 @@ describe('startIdleSpan', () => { it('Ensures idle span cannot exceed finalTimeout', () => { const transactions: Event[] = []; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transactions.push(event); return null; }); @@ -282,7 +283,7 @@ describe('startIdleSpan', () => { startTime: baseTimeInSeconds + 4, }); - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(regularSpan.isRecording()).toBe(false); expect(idleSpan.isRecording()).toBe(false); @@ -334,7 +335,7 @@ describe('startIdleSpan', () => { expect(hookSpans).toEqual([{ span: idleSpan, hook: 'spanStart' }]); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); expect(spanToJSON(idleSpan).timestamp).toBeDefined(); expect(hookSpans).toEqual([ @@ -352,7 +353,7 @@ describe('startIdleSpan', () => { setCurrentClient(client); client.init(); - const recordDroppedEventSpy = jest.spyOn(client, 'recordDroppedEvent'); + const recordDroppedEventSpy = vi.spyOn(client, 'recordDroppedEvent'); const idleSpan = startIdleSpan({ name: 'idle span' }); expect(idleSpan).toBeDefined(); @@ -364,7 +365,7 @@ describe('startIdleSpan', () => { it('sets finish reason when span is ended manually', () => { let transaction: Event | undefined; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transaction = event; return null; }); @@ -375,7 +376,7 @@ describe('startIdleSpan', () => { const span = startIdleSpan({ name: 'foo' }); span.end(); - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(beforeSendTransaction).toHaveBeenCalledTimes(1); expect(transaction?.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON]).toEqual( @@ -385,7 +386,7 @@ describe('startIdleSpan', () => { it('sets finish reason when span ends', () => { let transaction: Event | undefined; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transaction = event; return null; }); @@ -396,7 +397,7 @@ describe('startIdleSpan', () => { startIdleSpan({ name: 'foo' }); startSpan({ name: 'inner' }, () => {}); - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(beforeSendTransaction).toHaveBeenCalledTimes(1); expect(transaction?.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON]).toEqual( @@ -406,7 +407,7 @@ describe('startIdleSpan', () => { it('sets finish reason when span ends via expired heartbeat timeout', () => { let transaction: Event | undefined; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transaction = event; return null; }); @@ -417,7 +418,7 @@ describe('startIdleSpan', () => { startIdleSpan({ name: 'foo' }); startSpanManual({ name: 'inner' }, () => {}); - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(beforeSendTransaction).toHaveBeenCalledTimes(1); expect(transaction?.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON]).toEqual( @@ -427,7 +428,7 @@ describe('startIdleSpan', () => { it('sets finish reason when span ends via final timeout', () => { let transaction: Event | undefined; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transaction = event; return null; }); @@ -439,15 +440,15 @@ describe('startIdleSpan', () => { startIdleSpan({ name: 'foo' }, { finalTimeout: TRACING_DEFAULTS.childSpanTimeout * 2 }); const span1 = startInactiveSpan({ name: 'inner' }); - jest.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1); + vi.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1); span1.end(); const span2 = startInactiveSpan({ name: 'inner2' }); - jest.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1); + vi.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1); span2.end(); startInactiveSpan({ name: 'inner3' }); - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(beforeSendTransaction).toHaveBeenCalledTimes(1); expect(transaction?.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON]).toEqual( @@ -457,7 +458,7 @@ describe('startIdleSpan', () => { it('uses finish reason set outside when span ends', () => { let transaction: Event | undefined; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transaction = event; return null; }); @@ -469,7 +470,7 @@ describe('startIdleSpan', () => { const span = startIdleSpan({ name: 'foo' }); span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON, 'custom reason'); startSpan({ name: 'inner' }, () => {}); - jest.runOnlyPendingTimers(); + vi.runOnlyPendingTimers(); expect(beforeSendTransaction).toHaveBeenCalledTimes(1); expect(transaction?.contexts?.trace?.data?.[SEMANTIC_ATTRIBUTE_SENTRY_IDLE_SPAN_FINISH_REASON]).toEqual( @@ -482,7 +483,7 @@ describe('startIdleSpan', () => { const idleSpan = startIdleSpan({ name: 'idle span' }); expect(idleSpan).toBeDefined(); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); expect(spanToJSON(idleSpan).timestamp).toBeDefined(); }); @@ -492,7 +493,7 @@ describe('startIdleSpan', () => { startInactiveSpan({ name: 'span' }); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); }); @@ -503,11 +504,11 @@ describe('startIdleSpan', () => { startSpan({ name: 'span1' }, () => {}); - jest.advanceTimersByTime(2); + vi.advanceTimersByTime(2); startSpan({ name: 'span2' }, () => {}); - jest.advanceTimersByTime(8); + vi.advanceTimersByTime(8); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); }); @@ -519,11 +520,11 @@ describe('startIdleSpan', () => { startSpan({ name: 'span1' }, () => {}); - jest.advanceTimersByTime(2); + vi.advanceTimersByTime(2); startSpan({ name: 'span2' }, () => {}); - jest.advanceTimersByTime(10); + vi.advanceTimersByTime(10); expect(spanToJSON(idleSpan).timestamp).toBeDefined(); }); @@ -541,12 +542,12 @@ describe('startIdleSpan', () => { expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); // Wait some time - jest.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1000); + vi.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1000); expect(spanToJSON(idleSpan).status).not.toEqual('deadline_exceeded'); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); // Wait for timeout to exceed - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); expect(spanToJSON(idleSpan).status).not.toEqual('deadline_exceeded'); expect(spanToJSON(idleSpan).timestamp).toBeDefined(); }); @@ -562,26 +563,26 @@ describe('startIdleSpan', () => { expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); // Wait some time - jest.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1000); + vi.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1000); expect(spanToJSON(idleSpan).status).not.toEqual('deadline_exceeded'); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); // New span resets the timeout startInactiveSpan({ name: 'span' }); - jest.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1000); + vi.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1000); expect(spanToJSON(idleSpan).status).not.toEqual('deadline_exceeded'); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); // New span resets the timeout startInactiveSpan({ name: 'span' }); - jest.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1000); + vi.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout - 1000); expect(spanToJSON(idleSpan).status).not.toEqual('deadline_exceeded'); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); // Wait for timeout to exceed - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); expect(spanToJSON(idleSpan).status).not.toEqual('deadline_exceeded'); expect(spanToJSON(idleSpan).timestamp).toBeDefined(); }); @@ -592,16 +593,16 @@ describe('startIdleSpan', () => { const idleSpan = startIdleSpan({ name: 'idle span' }, { disableAutoFinish: true }); expect(idleSpan).toBeDefined(); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); // Now emit a signal getClient()!.emit('idleSpanEnableAutoFinish', idleSpan); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); expect(spanToJSON(idleSpan).timestamp).toBeDefined(); }); @@ -611,16 +612,16 @@ describe('startIdleSpan', () => { startInactiveSpan({ name: 'inner' }); - jest.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); - jest.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); // Now emit a signal getClient()!.emit('idleSpanEnableAutoFinish', idleSpan); - jest.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.childSpanTimeout); expect(spanToJSON(idleSpan).timestamp).toBeDefined(); }); @@ -628,7 +629,7 @@ describe('startIdleSpan', () => { const idleSpan = startIdleSpan({ name: 'idle span' }, { disableAutoFinish: true }); expect(idleSpan).toBeDefined(); - jest.advanceTimersByTime(TRACING_DEFAULTS.finalTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.finalTimeout); expect(spanToJSON(idleSpan).timestamp).toBeDefined(); }); @@ -637,17 +638,17 @@ describe('startIdleSpan', () => { const idleSpan = startIdleSpan({ name: 'idle span' }, { disableAutoFinish: true }); expect(idleSpan).toBeDefined(); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); // Now emit a signal, but with a different span getClient()!.emit('idleSpanEnableAutoFinish', span); // This doesn't affect us! - jest.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); + vi.advanceTimersByTime(TRACING_DEFAULTS.idleTimeout); expect(spanToJSON(idleSpan).timestamp).toBeUndefined(); }); }); @@ -668,7 +669,7 @@ describe('startIdleSpan', () => { expect(getActiveSpan()).toBe(idleSpan); - jest.runAllTimers(); + vi.runAllTimers(); expect(spanToJSON(idleSpan!).timestamp).toBe(1100); }); @@ -688,7 +689,7 @@ describe('startIdleSpan', () => { expect(getActiveSpan()).toBe(idleSpan); - jest.runAllTimers(); + vi.runAllTimers(); expect(spanToJSON(idleSpan!).timestamp).toBe(1030); }); @@ -708,7 +709,7 @@ describe('startIdleSpan', () => { expect(getActiveSpan()).toBe(idleSpan); - jest.runAllTimers(); + vi.runAllTimers(); expect(spanToJSON(idleSpan!).timestamp).toBeLessThan(999_999_999); expect(spanToJSON(idleSpan!).timestamp).toBeGreaterThan(1060); diff --git a/packages/core/test/lib/tracing/sentryNonRecordingSpan.test.ts b/packages/core/test/lib/tracing/sentryNonRecordingSpan.test.ts index 71bb78738f4f..95a9591ed443 100644 --- a/packages/core/test/lib/tracing/sentryNonRecordingSpan.test.ts +++ b/packages/core/test/lib/tracing/sentryNonRecordingSpan.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { SPAN_STATUS_ERROR } from '../../../src/tracing'; import { SentryNonRecordingSpan } from '../../../src/tracing/sentryNonRecordingSpan'; import type { Span } from '../../../src/types-hoist'; diff --git a/packages/core/test/lib/tracing/sentrySpan.test.ts b/packages/core/test/lib/tracing/sentrySpan.test.ts index cfc3745f4387..eda0246bda03 100644 --- a/packages/core/test/lib/tracing/sentrySpan.test.ts +++ b/packages/core/test/lib/tracing/sentrySpan.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, test, vi } from 'vitest'; import type { SpanJSON } from '../../../src'; import { SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, getCurrentScope, setCurrentClient, timestampInSeconds } from '../../../src'; import { SentrySpan } from '../../../src/tracing/sentrySpan'; @@ -130,7 +131,7 @@ describe('SentrySpan', () => { setCurrentClient(client); // @ts-expect-error Accessing private transport API - const mockSend = jest.spyOn(client._transport, 'send'); + const mockSend = vi.spyOn(client._transport, 'send'); const notSampledSpan = new SentrySpan({ name: 'not-sampled', @@ -154,7 +155,7 @@ describe('SentrySpan', () => { }); test('sends the span if `beforeSendSpan` does not modify the span', () => { - const beforeSendSpan = jest.fn(span => span); + const beforeSendSpan = vi.fn(span => span); const client = new TestClient( getDefaultTestClientOptions({ dsn: 'https://username@domain/123', @@ -165,7 +166,7 @@ describe('SentrySpan', () => { setCurrentClient(client); // @ts-expect-error Accessing private transport API - const mockSend = jest.spyOn(client._transport, 'send'); + const mockSend = vi.spyOn(client._transport, 'send'); const span = new SentrySpan({ name: 'test', isStandalone: true, @@ -178,9 +179,9 @@ describe('SentrySpan', () => { }); test('does not drop the span if `beforeSendSpan` returns null', () => { - const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => undefined); + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => undefined); - const beforeSendSpan = jest.fn(() => null as unknown as SpanJSON); + const beforeSendSpan = vi.fn(() => null as unknown as SpanJSON); const client = new TestClient( getDefaultTestClientOptions({ dsn: 'https://username@domain/123', @@ -190,9 +191,9 @@ describe('SentrySpan', () => { ); setCurrentClient(client); - const recordDroppedEventSpy = jest.spyOn(client, 'recordDroppedEvent'); + const recordDroppedEventSpy = vi.spyOn(client, 'recordDroppedEvent'); // @ts-expect-error Accessing private transport API - const mockSend = jest.spyOn(client._transport, 'send'); + const mockSend = vi.spyOn(client._transport, 'send'); const span = new SentrySpan({ name: 'test', isStandalone: true, @@ -220,7 +221,7 @@ describe('SentrySpan', () => { setCurrentClient(client); const scope = getCurrentScope(); - const captureEventSpy = jest.spyOn(scope, 'captureEvent').mockImplementation(() => 'testId'); + const captureEventSpy = vi.spyOn(scope, 'captureEvent').mockImplementation(() => 'testId'); const span = new SentrySpan({ name: 'test', diff --git a/packages/core/test/lib/tracing/spanstatus.test.ts b/packages/core/test/lib/tracing/spanstatus.test.ts index 2ed356b1d73e..ee6bfd59443f 100644 --- a/packages/core/test/lib/tracing/spanstatus.test.ts +++ b/packages/core/test/lib/tracing/spanstatus.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { SentrySpan, setHttpStatus, spanToJSON } from '../../../src/index'; describe('setHttpStatus', () => { diff --git a/packages/core/test/lib/tracing/trace.test.ts b/packages/core/test/lib/tracing/trace.test.ts index e15bf146bee6..81d1258517d3 100644 --- a/packages/core/test/lib/tracing/trace.test.ts +++ b/packages/core/test/lib/tracing/trace.test.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, @@ -54,7 +55,7 @@ describe('startSpan', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe.each([ @@ -620,7 +621,7 @@ describe('startSpan', () => { }); it('samples with a tracesSampler', () => { - const tracesSampler = jest.fn(() => { + const tracesSampler = vi.fn(() => { return true; }); @@ -647,7 +648,7 @@ describe('startSpan', () => { }); it('includes the scope at the time the span was started when finished', async () => { - const beforeSendTransaction = jest.fn(event => event); + const beforeSendTransaction = vi.fn(event => event); const client = new TestClient( getDefaultTestClientOptions({ @@ -697,7 +698,7 @@ describe('startSpan', () => { const carrier = getMainCarrier(); - const customFn = jest.fn((_options: StartSpanOptions, callback: (span: Span) => string) => { + const customFn = vi.fn((_options: StartSpanOptions, callback: (span: Span) => string) => { callback(staticSpan); return 'aha'; }) as typeof startSpan; @@ -1188,7 +1189,7 @@ describe('startSpanManual', () => { const carrier = getMainCarrier(); - const customFn = jest.fn((_options: StartSpanOptions, callback: (span: Span) => string) => { + const customFn = vi.fn((_options: StartSpanOptions, callback: (span: Span) => string) => { callback(staticSpan); return 'aha'; }) as unknown as typeof startSpanManual; @@ -1543,7 +1544,7 @@ describe('startInactiveSpan', () => { }); it('includes the scope at the time the span was started when finished', async () => { - const beforeSendTransaction = jest.fn(event => event); + const beforeSendTransaction = vi.fn(event => event); const client = new TestClient( getDefaultTestClientOptions({ @@ -1600,7 +1601,7 @@ describe('startInactiveSpan', () => { const carrier = getMainCarrier(); - const customFn = jest.fn((_options: StartSpanOptions) => { + const customFn = vi.fn((_options: StartSpanOptions) => { return staticSpan; }) as unknown as typeof startInactiveSpan; @@ -1765,7 +1766,7 @@ describe('getActiveSpan', () => { const carrier = getMainCarrier(); - const customFn = jest.fn(() => { + const customFn = vi.fn(() => { return staticSpan; }) as typeof getActiveSpan; @@ -1832,7 +1833,7 @@ describe('withActiveSpan()', () => { const carrier = getMainCarrier(); - const customFn = jest.fn((_span: Span | null, callback: (scope: Scope) => string) => { + const customFn = vi.fn((_span: Span | null, callback: (scope: Scope) => string) => { callback(staticScope); return 'aha'; }) as typeof withActiveSpan; @@ -1864,7 +1865,7 @@ describe('span hooks', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('correctly emits span hooks', () => { @@ -1915,7 +1916,7 @@ describe('suppressTracing', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('works for a root span', () => { diff --git a/packages/core/test/lib/transports/base.test.ts b/packages/core/test/lib/transports/base.test.ts index 70179f535efe..3d9ba19e0455 100644 --- a/packages/core/test/lib/transports/base.test.ts +++ b/packages/core/test/lib/transports/base.test.ts @@ -1,5 +1,6 @@ import type { AttachmentItem, EventEnvelope, EventItem, TransportMakeRequestResponse } from '../../../src/types-hoist'; +import { describe, expect, it, vi } from 'vitest'; import { createTransport } from '../../../src/transports/base'; import { createEnvelope, serializeEnvelope } from '../../../src/utils-hoist/envelope'; import type { PromiseBuffer } from '../../../src/utils-hoist/promisebuffer'; @@ -38,8 +39,8 @@ describe('createTransport', () => { it('flushes the buffer', async () => { const mockBuffer: PromiseBuffer = { $: [], - add: jest.fn(), - drain: jest.fn(), + add: vi.fn(), + drain: vi.fn(), }; const transport = createTransport(transportOptions, _ => resolvedSyncPromise({}), mockBuffer); /* eslint-disable @typescript-eslint/unbound-method */ @@ -94,11 +95,11 @@ describe('createTransport', () => { transportResponse = res; } - const mockRequestExecutor = jest.fn(_ => { + const mockRequestExecutor = vi.fn(_ => { return resolvedSyncPromise(transportResponse); }); - const mockRecordDroppedEventCallback = jest.fn(); + const mockRecordDroppedEventCallback = vi.fn(); const transport = createTransport({ recordDroppedEvent: mockRecordDroppedEventCallback }, mockRequestExecutor); @@ -109,7 +110,7 @@ describe('createTransport', () => { const { retryAfterSeconds, beforeLimit, withinLimit, afterLimit } = setRateLimitTimes(); const [transport, setTransportResponse, requestExecutor, recordDroppedEventCallback] = createTestTransport({}); - const dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => beforeLimit); + const dateNowSpy = vi.spyOn(Date, 'now').mockImplementation(() => beforeLimit); await transport.send(ERROR_ENVELOPE); expect(requestExecutor).toHaveBeenCalledTimes(1); @@ -156,7 +157,7 @@ describe('createTransport', () => { }, }); - const dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => beforeLimit); + const dateNowSpy = vi.spyOn(Date, 'now').mockImplementation(() => beforeLimit); await transport.send(ERROR_ENVELOPE); expect(requestExecutor).toHaveBeenCalledTimes(1); @@ -204,7 +205,7 @@ describe('createTransport', () => { }, }); - const dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => beforeLimit); + const dateNowSpy = vi.spyOn(Date, 'now').mockImplementation(() => beforeLimit); await transport.send(ERROR_ENVELOPE); expect(requestExecutor).toHaveBeenCalledTimes(1); @@ -264,7 +265,7 @@ describe('createTransport', () => { }, }); - const dateNowSpy = jest.spyOn(Date, 'now').mockImplementation(() => beforeLimit); + const dateNowSpy = vi.spyOn(Date, 'now').mockImplementation(() => beforeLimit); await transport.send(ERROR_ENVELOPE); expect(requestExecutor).toHaveBeenCalledTimes(1); diff --git a/packages/core/test/lib/transports/multiplexed.test.ts b/packages/core/test/lib/transports/multiplexed.test.ts index 024ea7e4bd8a..99e150f8bc66 100644 --- a/packages/core/test/lib/transports/multiplexed.test.ts +++ b/packages/core/test/lib/transports/multiplexed.test.ts @@ -8,6 +8,7 @@ import type { Transport, } from '../../../src/types-hoist'; +import { describe, expect, it, vi } from 'vitest'; import { createClientReportEnvelope, createEnvelope, @@ -95,7 +96,7 @@ describe('makeMultiplexedTransport', () => { it('Falls back to options DSN when a matched DSN is invalid', async () => { // Hide warning logs in the test - jest.spyOn(console, 'error').mockImplementation(() => {}); + vi.spyOn(console, 'error').mockImplementation(() => {}); expect.assertions(1); @@ -109,7 +110,7 @@ describe('makeMultiplexedTransport', () => { const transport = makeTransport({ url: DSN1_URL, ...transportOptions }); await transport.send(ERROR_ENVELOPE); - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('DSN can be overridden via match callback', async () => { diff --git a/packages/core/test/lib/transports/offline.test.ts b/packages/core/test/lib/transports/offline.test.ts index 2f4df73ddae9..0dfc550fcd38 100644 --- a/packages/core/test/lib/transports/offline.test.ts +++ b/packages/core/test/lib/transports/offline.test.ts @@ -9,6 +9,7 @@ import type { TransportMakeRequestResponse, } from '../../../src/types-hoist'; +import { describe, expect, it } from 'vitest'; import { createClientReportEnvelope, createEnvelope, diff --git a/packages/core/test/lib/trpc.test.ts b/packages/core/test/lib/trpc.test.ts new file mode 100644 index 000000000000..bb77dda8de17 --- /dev/null +++ b/packages/core/test/lib/trpc.test.ts @@ -0,0 +1,139 @@ +import { describe, test, expect, beforeEach, vi } from 'vitest'; +import { trpcMiddleware, setCurrentClient, type Client, type Span } from '../../src'; +import * as tracing from '../../src/tracing'; +import * as currentScopes from '../../src/currentScopes'; +import * as exports from '../../src/exports'; +import { getDefaultTestClientOptions, TestClient } from '../mocks/client'; + +describe('trpcMiddleware', () => { + let client: Client; + + const mockClient = { + getOptions: vi.fn().mockReturnValue({ + normalizeDepth: 3, + sendDefaultPii: false, + }), + captureException: vi.fn(), + } as unknown as Client; + + const mockSpan = { + end: vi.fn(), + } as unknown as Span; + + const mockScope = { + setContext: vi.fn(), + setTag: vi.fn(), + setExtra: vi.fn(), + }; + + const withScope = vi.fn(callback => { + return callback(mockScope); + }); + + beforeEach(() => { + vi.clearAllMocks(); + const options = getDefaultTestClientOptions({ tracesSampleRate: 1 }); + client = new TestClient(options); + setCurrentClient(client); + client.init(); + vi.spyOn(currentScopes, 'getClient').mockReturnValue(mockClient); + vi.spyOn(tracing, 'startSpanManual').mockImplementation((name, callback) => callback(mockSpan, () => {})); + vi.spyOn(currentScopes, 'withScope').mockImplementation(withScope); + vi.spyOn(exports, 'captureException').mockImplementation(() => 'mock-event-id'); + }); + + test('creates span with correct attributes', async () => { + const middleware = trpcMiddleware(); + const next = vi.fn().mockResolvedValue({ ok: true }); + + await middleware({ + path: 'test.procedure', + type: 'query', + next, + }); + + expect(tracing.startSpanManual).toHaveBeenCalledWith( + { + name: 'trpc/test.procedure', + op: 'rpc.server', + attributes: { + 'sentry.origin': 'auto.rpc.trpc', + 'sentry.source': 'route', + }, + forceTransaction: false, + }, + expect.any(Function), + ); + }); + + test('captures error when next() returns error result', async () => { + const middleware = trpcMiddleware(); + const error = new Error('Test error'); + const next = vi.fn().mockResolvedValue({ ok: false, error }); + + await middleware({ + path: 'test.procedure', + type: 'query', + next, + }); + + expect(exports.captureException).toHaveBeenCalledWith(error, { + mechanism: { handled: false, data: { function: 'trpcMiddleware' } }, + }); + }); + + test('sets correct context data with rpc input', async () => { + const middleware = trpcMiddleware({ attachRpcInput: true }); + const next = vi.fn().mockResolvedValue({ ok: true }); + const input = { foo: 'bar' }; + + await middleware({ + path: 'test.procedure', + type: 'query', + next, + rawInput: input, + }); + + expect(mockScope.setContext).toHaveBeenCalledWith('trpc', { + procedure_path: 'test.procedure', + procedure_type: 'query', + input: { foo: 'bar' }, + }); + }); + + test('handles thrown errors', async () => { + const middleware = trpcMiddleware(); + const error = new Error('Test error'); + const next = vi.fn().mockRejectedValue(error); + + await expect( + middleware({ + path: 'test.procedure', + type: 'query', + next, + }), + ).rejects.toThrow(error); + + expect(exports.captureException).toHaveBeenCalledWith(error, { + mechanism: { handled: false, data: { function: 'trpcMiddleware' } }, + }); + }); + + test('respects forceTransaction option', async () => { + const middleware = trpcMiddleware({ forceTransaction: true }); + const next = vi.fn().mockResolvedValue({ ok: true }); + + await middleware({ + path: 'test.procedure', + type: 'query', + next, + }); + + expect(tracing.startSpanManual).toHaveBeenCalledWith( + expect.objectContaining({ + forceTransaction: true, + }), + expect.any(Function), + ); + }); +}); diff --git a/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts b/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts index 077190670aba..2fb8b2f93d16 100644 --- a/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts +++ b/packages/core/test/lib/utils/applyScopeDataToEvent.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { ScopeData } from '../../../src'; import { startInactiveSpan } from '../../../src'; import type { Attachment, Breadcrumb, Event, EventProcessor, EventType } from '../../../src/types-hoist'; diff --git a/packages/core/test/lib/utils/cookie.test.ts b/packages/core/test/lib/utils/cookie.test.ts index b41d2a4fe112..ccec4a9a26dd 100644 --- a/packages/core/test/lib/utils/cookie.test.ts +++ b/packages/core/test/lib/utils/cookie.test.ts @@ -28,6 +28,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { describe, expect, it } from 'vitest'; import { parseCookie } from '../../../src/utils/cookie'; describe('parseCookie(str)', function () { diff --git a/packages/core/test/lib/utils/handleCallbackErrors.test.ts b/packages/core/test/lib/utils/handleCallbackErrors.test.ts index 08c116e50ceb..6c4815f35680 100644 --- a/packages/core/test/lib/utils/handleCallbackErrors.test.ts +++ b/packages/core/test/lib/utils/handleCallbackErrors.test.ts @@ -1,10 +1,11 @@ +import { describe, expect, it, vi } from 'vitest'; import { handleCallbackErrors } from '../../../src/utils/handleCallbackErrors'; describe('handleCallbackErrors', () => { it('works with a simple callback', () => { - const onError = jest.fn(); + const onError = vi.fn(); - const fn = jest.fn(() => 'aa'); + const fn = vi.fn(() => 'aa'); const res = handleCallbackErrors(fn, onError); @@ -16,9 +17,9 @@ describe('handleCallbackErrors', () => { it('triggers onError when callback has sync error', () => { const error = new Error('test error'); - const onError = jest.fn(); + const onError = vi.fn(); - const fn = jest.fn(() => { + const fn = vi.fn(() => { throw error; }); @@ -30,9 +31,9 @@ describe('handleCallbackErrors', () => { }); it('works with an async callback', async () => { - const onError = jest.fn(); + const onError = vi.fn(); - const fn = jest.fn(async () => 'aa'); + const fn = vi.fn(async () => 'aa'); const res = handleCallbackErrors(fn, onError); @@ -46,11 +47,11 @@ describe('handleCallbackErrors', () => { }); it('triggers onError when callback returns promise that rejects', async () => { - const onError = jest.fn(); + const onError = vi.fn(); const error = new Error('test error'); - const fn = jest.fn(async () => { + const fn = vi.fn(async () => { await new Promise(resolve => setTimeout(resolve, 10)); throw error; }); @@ -70,10 +71,10 @@ describe('handleCallbackErrors', () => { describe('onFinally', () => { it('triggers after successful sync callback', () => { - const onError = jest.fn(); - const onFinally = jest.fn(); + const onError = vi.fn(); + const onFinally = vi.fn(); - const fn = jest.fn(() => 'aa'); + const fn = vi.fn(() => 'aa'); const res = handleCallbackErrors(fn, onError, onFinally); @@ -86,10 +87,10 @@ describe('handleCallbackErrors', () => { it('triggers after error in sync callback', () => { const error = new Error('test error'); - const onError = jest.fn(); - const onFinally = jest.fn(); + const onError = vi.fn(); + const onFinally = vi.fn(); - const fn = jest.fn(() => { + const fn = vi.fn(() => { throw error; }); @@ -102,10 +103,10 @@ describe('handleCallbackErrors', () => { }); it('triggers after successful async callback', async () => { - const onError = jest.fn(); - const onFinally = jest.fn(); + const onError = vi.fn(); + const onFinally = vi.fn(); - const fn = jest.fn(async () => 'aa'); + const fn = vi.fn(async () => 'aa'); const res = handleCallbackErrors(fn, onError, onFinally); @@ -122,12 +123,12 @@ describe('handleCallbackErrors', () => { }); it('triggers after error in async callback', async () => { - const onError = jest.fn(); - const onFinally = jest.fn(); + const onError = vi.fn(); + const onFinally = vi.fn(); const error = new Error('test error'); - const fn = jest.fn(async () => { + const fn = vi.fn(async () => { await new Promise(resolve => setTimeout(resolve, 10)); throw error; }); diff --git a/packages/core/test/lib/utils/hasTracingEnabled.test.ts b/packages/core/test/lib/utils/hasTracingEnabled.test.ts index 99062b437ec8..ef6ff6f26624 100644 --- a/packages/core/test/lib/utils/hasTracingEnabled.test.ts +++ b/packages/core/test/lib/utils/hasTracingEnabled.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { hasSpansEnabled } from '../../../src'; import { hasTracingEnabled } from '../../../src'; diff --git a/packages/core/test/lib/utils/isSentryRequestUrl.test.ts b/packages/core/test/lib/utils/isSentryRequestUrl.test.ts index c20f8bc011fa..7a07a6041293 100644 --- a/packages/core/test/lib/utils/isSentryRequestUrl.test.ts +++ b/packages/core/test/lib/utils/isSentryRequestUrl.test.ts @@ -1,5 +1,6 @@ import type { Client } from '../../../src/client'; +import { describe, expect, it } from 'vitest'; import { isSentryRequestUrl } from '../../../src'; describe('isSentryRequestUrl', () => { diff --git a/packages/core/test/lib/utils/merge.test.ts b/packages/core/test/lib/utils/merge.test.ts index b0a215231bfb..e89ff2e86561 100644 --- a/packages/core/test/lib/utils/merge.test.ts +++ b/packages/core/test/lib/utils/merge.test.ts @@ -1,5 +1,7 @@ import { merge } from '../../../src/utils/merge'; +import { describe, expect, it } from 'vitest'; + describe('merge', () => { it('works with empty objects', () => { const oldData = {}; diff --git a/packages/core/test/lib/utils/meta.test.ts b/packages/core/test/lib/utils/meta.test.ts index 3d78247b8951..19fb68ef0e7d 100644 --- a/packages/core/test/lib/utils/meta.test.ts +++ b/packages/core/test/lib/utils/meta.test.ts @@ -1,19 +1,21 @@ +import { describe, expect, it, vi } from 'vitest'; import { getTraceMetaTags } from '../../../src/utils/meta'; import * as TraceDataModule from '../../../src/utils/traceData'; describe('getTraceMetaTags', () => { it('renders baggage and sentry-trace values to stringified Html meta tags', () => { - jest.spyOn(TraceDataModule, 'getTraceData').mockReturnValueOnce({ + vi.spyOn(TraceDataModule, 'getTraceData').mockReturnValueOnce({ 'sentry-trace': '12345678901234567890123456789012-1234567890123456-1', baggage: 'sentry-environment=production', }); - expect(getTraceMetaTags()).toBe(` + expect(getTraceMetaTags()) + .toBe(` `); }); it('renders just sentry-trace values to stringified Html meta tags', () => { - jest.spyOn(TraceDataModule, 'getTraceData').mockReturnValueOnce({ + vi.spyOn(TraceDataModule, 'getTraceData').mockReturnValueOnce({ 'sentry-trace': '12345678901234567890123456789012-1234567890123456-1', }); @@ -23,7 +25,7 @@ describe('getTraceMetaTags', () => { }); it('returns an empty string if neither sentry-trace nor baggage values are available', () => { - jest.spyOn(TraceDataModule, 'getTraceData').mockReturnValueOnce({}); + vi.spyOn(TraceDataModule, 'getTraceData').mockReturnValueOnce({}); expect(getTraceMetaTags()).toBe(''); }); diff --git a/packages/core/test/lib/utils/parameterize.test.ts b/packages/core/test/lib/utils/parameterize.test.ts index c772c2a410c3..e79e48e84afa 100644 --- a/packages/core/test/lib/utils/parameterize.test.ts +++ b/packages/core/test/lib/utils/parameterize.test.ts @@ -1,5 +1,6 @@ import type { ParameterizedString } from '../../../src/types-hoist'; +import { describe, expect, test } from 'vitest'; import { parameterize } from '../../../src/utils/parameterize'; describe('parameterize()', () => { diff --git a/packages/core/test/lib/utils/parseSampleRate.test.ts b/packages/core/test/lib/utils/parseSampleRate.test.ts index 9c0d91b1810e..fae94a6cc354 100644 --- a/packages/core/test/lib/utils/parseSampleRate.test.ts +++ b/packages/core/test/lib/utils/parseSampleRate.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { parseSampleRate } from '../../../src/utils/parseSampleRate'; describe('parseSampleRate', () => { diff --git a/packages/core/test/lib/utils/request.test.ts b/packages/core/test/lib/utils/request.test.ts index f1d62a7f2a73..44682e6e9b0f 100644 --- a/packages/core/test/lib/utils/request.test.ts +++ b/packages/core/test/lib/utils/request.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { extractQueryParamsFromUrl, headersToDict, diff --git a/packages/core/test/lib/utils/spanUtils.test.ts b/packages/core/test/lib/utils/spanUtils.test.ts index acbac2b35dbd..55d54c3d269e 100644 --- a/packages/core/test/lib/utils/spanUtils.test.ts +++ b/packages/core/test/lib/utils/spanUtils.test.ts @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it, test } from 'vitest'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, diff --git a/packages/core/test/lib/utils/traceData.test.ts b/packages/core/test/lib/utils/traceData.test.ts index 78c8d806be2a..a7a0c218f069 100644 --- a/packages/core/test/lib/utils/traceData.test.ts +++ b/packages/core/test/lib/utils/traceData.test.ts @@ -1,3 +1,4 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import type { Client } from '../../../src/'; import { SentrySpan, @@ -48,7 +49,7 @@ describe('getTraceData', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('uses the ACS implementation, if available', () => { @@ -56,7 +57,7 @@ describe('getTraceData', () => { const carrier = getMainCarrier(); - const customFn = jest.fn((options?: { span?: Span }) => { + const customFn = vi.fn((options?: { span?: Span }) => { expect(options).toEqual({ span: undefined }); return { 'sentry-trace': 'abc', @@ -97,7 +98,7 @@ describe('getTraceData', () => { sampled: true, }); - const customFn = jest.fn((options?: { span?: Span }) => { + const customFn = vi.fn((options?: { span?: Span }) => { expect(options).toEqual({ span }); return { 'sentry-trace': 'abc', diff --git a/packages/core/test/lib/utils/transactionEvent.test.ts b/packages/core/test/lib/utils/transactionEvent.test.ts index cd5a3cd750c5..fb49067ff273 100644 --- a/packages/core/test/lib/utils/transactionEvent.test.ts +++ b/packages/core/test/lib/utils/transactionEvent.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { SEMANTIC_ATTRIBUTE_EXCLUSIVE_TIME, SEMANTIC_ATTRIBUTE_PROFILE_ID } from '../../../src/semanticAttributes'; import type { SpanJSON, TransactionEvent } from '../../../src/types-hoist'; import {} from '../../../src/types-hoist'; diff --git a/packages/core/test/lib/vendor/getClientIpAddress.test.ts b/packages/core/test/lib/vendor/getClientIpAddress.test.ts index 91c5b1961485..ee530566ad60 100644 --- a/packages/core/test/lib/vendor/getClientIpAddress.test.ts +++ b/packages/core/test/lib/vendor/getClientIpAddress.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getClientIPAddress } from '../../../src/vendor/getIpAddress'; describe('getClientIPAddress', () => { diff --git a/packages/core/test/utils-hoist/aggregate-errors.test.ts b/packages/core/test/utils-hoist/aggregate-errors.test.ts index 535325bd0491..bc6aa2a99d19 100644 --- a/packages/core/test/utils-hoist/aggregate-errors.test.ts +++ b/packages/core/test/utils-hoist/aggregate-errors.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import type { Event, EventHint, Exception, ExtendedError, StackParser } from '../../src/types-hoist'; import { applyAggregateErrorsToEvent } from '../../src/utils-hoist/aggregate-errors'; import { createStackParser } from '../../src/utils-hoist/stacktrace'; diff --git a/packages/core/test/utils-hoist/baggage.test.ts b/packages/core/test/utils-hoist/baggage.test.ts index 9268e178b3a6..d24b11c9f62e 100644 --- a/packages/core/test/utils-hoist/baggage.test.ts +++ b/packages/core/test/utils-hoist/baggage.test.ts @@ -1,3 +1,4 @@ +import { expect, test } from 'vitest'; import { baggageHeaderToDynamicSamplingContext, dynamicSamplingContextToSentryBaggageHeader, diff --git a/packages/core/test/utils-hoist/breadcrumb-log-level.test.ts b/packages/core/test/utils-hoist/breadcrumb-log-level.test.ts index 6be625bb0a63..86062e025f37 100644 --- a/packages/core/test/utils-hoist/breadcrumb-log-level.test.ts +++ b/packages/core/test/utils-hoist/breadcrumb-log-level.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getBreadcrumbLogLevelFromHttpStatusCode } from '../../src/utils-hoist/breadcrumb-log-level'; describe('getBreadcrumbLogLevelFromHttpStatusCode()', () => { diff --git a/packages/core/test/utils-hoist/browser.test.ts b/packages/core/test/utils-hoist/browser.test.ts index 01a0d5eee747..58b9a0a34ee5 100644 --- a/packages/core/test/utils-hoist/browser.test.ts +++ b/packages/core/test/utils-hoist/browser.test.ts @@ -1,5 +1,6 @@ import { JSDOM } from 'jsdom'; +import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { htmlTreeAsString } from '../../src/utils-hoist/browser'; beforeAll(() => { diff --git a/packages/core/test/utils-hoist/clientreport.test.ts b/packages/core/test/utils-hoist/clientreport.test.ts index f925d74fa50c..371047b2ebad 100644 --- a/packages/core/test/utils-hoist/clientreport.test.ts +++ b/packages/core/test/utils-hoist/clientreport.test.ts @@ -1,5 +1,6 @@ import type { ClientReport } from '../../src/types-hoist'; +import { describe, expect, it } from 'vitest'; import { createClientReportEnvelope } from '../../src/utils-hoist/clientreport'; import { parseEnvelope, serializeEnvelope } from '../../src/utils-hoist/envelope'; diff --git a/packages/core/test/utils-hoist/dsn.test.ts b/packages/core/test/utils-hoist/dsn.test.ts index 6f70c83d0c60..86d7eb0a9552 100644 --- a/packages/core/test/utils-hoist/dsn.test.ts +++ b/packages/core/test/utils-hoist/dsn.test.ts @@ -1,17 +1,18 @@ +import { beforeEach, describe, expect, test, vi } from 'vitest'; import { DEBUG_BUILD } from '../../src/utils-hoist/debug-build'; import { dsnToString, makeDsn } from '../../src/utils-hoist/dsn'; import { logger } from '../../src/utils-hoist/logger'; -function testIf(condition: boolean): jest.It { +function testIf(condition: boolean) { return condition ? test : test.skip; } -const loggerErrorSpy = jest.spyOn(logger, 'error').mockImplementation(() => {}); -const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); +const loggerErrorSpy = vi.spyOn(logger, 'error').mockImplementation(() => {}); +const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); describe('Dsn', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('fromComponents', () => { diff --git a/packages/core/test/utils-hoist/envelope.test.ts b/packages/core/test/utils-hoist/envelope.test.ts index 6da22c1869a9..4a5264978e5f 100644 --- a/packages/core/test/utils-hoist/envelope.test.ts +++ b/packages/core/test/utils-hoist/envelope.test.ts @@ -7,6 +7,7 @@ import { spanToJSON, } from '@sentry/core'; import { SentrySpan } from '@sentry/core'; +import { describe, expect, it, test, vi, afterEach } from 'vitest'; import { getSentryCarrier } from '../../src/carrier'; import { addItemToEnvelope, @@ -110,10 +111,10 @@ describe('envelope', () => { before: () => { GLOBAL_OBJ.__SENTRY__ = {}; - getSentryCarrier(GLOBAL_OBJ).encodePolyfill = jest.fn((input: string) => + getSentryCarrier(GLOBAL_OBJ).encodePolyfill = vi.fn((input: string) => new TextEncoder().encode(input), ); - getSentryCarrier(GLOBAL_OBJ).decodePolyfill = jest.fn((input: Uint8Array) => + getSentryCarrier(GLOBAL_OBJ).decodePolyfill = vi.fn((input: Uint8Array) => new TextDecoder().decode(input), ); }, diff --git a/packages/core/test/utils-hoist/eventbuilder.test.ts b/packages/core/test/utils-hoist/eventbuilder.test.ts index 2994fb5520f6..877df25d8fed 100644 --- a/packages/core/test/utils-hoist/eventbuilder.test.ts +++ b/packages/core/test/utils-hoist/eventbuilder.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, test } from 'vitest'; import type { Client } from '../../src/client'; import { eventFromMessage, eventFromUnknownInput } from '../../src/utils-hoist/eventbuilder'; import { nodeStackLineParser } from '../../src/utils-hoist/node-stack-trace'; diff --git a/packages/core/test/utils-hoist/instrument.test.ts b/packages/core/test/utils-hoist/instrument.test.ts index 938ba8f17e3e..b6917994c96b 100644 --- a/packages/core/test/utils-hoist/instrument.test.ts +++ b/packages/core/test/utils-hoist/instrument.test.ts @@ -1,3 +1,4 @@ +import { describe, test } from 'vitest'; import { maybeInstrument } from '../../src/utils-hoist/instrument/handlers'; describe('maybeInstrument', () => { diff --git a/packages/core/test/utils-hoist/instrument/fetch.test.ts b/packages/core/test/utils-hoist/instrument/fetch.test.ts index fc6102d6b617..0f08d9f03c6e 100644 --- a/packages/core/test/utils-hoist/instrument/fetch.test.ts +++ b/packages/core/test/utils-hoist/instrument/fetch.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { parseFetchArgs } from '../../../src/utils-hoist/instrument/fetch'; describe('instrument > parseFetchArgs', () => { diff --git a/packages/core/test/utils-hoist/is.test.ts b/packages/core/test/utils-hoist/is.test.ts index 44feba601d04..09fac86fcf12 100644 --- a/packages/core/test/utils-hoist/is.test.ts +++ b/packages/core/test/utils-hoist/is.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, test } from 'vitest'; import { isDOMError, isDOMException, diff --git a/packages/core/test/utils-hoist/lru.test.ts b/packages/core/test/utils-hoist/lru.test.ts index 39c83a1fbf8f..28259a2700ad 100644 --- a/packages/core/test/utils-hoist/lru.test.ts +++ b/packages/core/test/utils-hoist/lru.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { LRUMap } from '../../src/utils-hoist/lru'; describe('LRUMap', () => { diff --git a/packages/core/test/utils-hoist/misc.test.ts b/packages/core/test/utils-hoist/misc.test.ts index 05f8736bc443..ea2d03992fe8 100644 --- a/packages/core/test/utils-hoist/misc.test.ts +++ b/packages/core/test/utils-hoist/misc.test.ts @@ -1,5 +1,6 @@ import type { Event, Mechanism, StackFrame } from '../../src/types-hoist'; +import { describe, expect, it, test } from 'vitest'; import { addContextToFrame, addExceptionMechanism, @@ -302,10 +303,10 @@ describe('uuid4 generation', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const cryptoMod = require('crypto'); - (global as any).crypto = { getRandomValues: cryptoMod.getRandomValues }; + const crypto = { getRandomValues: cryptoMod.getRandomValues }; for (let index = 0; index < 1_000; index++) { - expect(uuid4()).toMatch(uuid4Regex); + expect(uuid4(crypto)).toMatch(uuid4Regex); } }); @@ -313,23 +314,23 @@ describe('uuid4 generation', () => { // eslint-disable-next-line @typescript-eslint/no-var-requires const cryptoMod = require('crypto'); - (global as any).crypto = { randomUUID: cryptoMod.randomUUID }; + const crypto = { getRandomValues: cryptoMod.getRandomValues, randomUUID: cryptoMod.randomUUID }; for (let index = 0; index < 1_000; index++) { - expect(uuid4()).toMatch(uuid4Regex); + expect(uuid4(crypto)).toMatch(uuid4Regex); } }); it("return valid uuid v4 even if crypto doesn't exists", () => { - (global as any).crypto = { getRandomValues: undefined, randomUUID: undefined }; + const crypto = { getRandomValues: undefined, randomUUID: undefined }; for (let index = 0; index < 1_000; index++) { - expect(uuid4()).toMatch(uuid4Regex); + expect(uuid4(crypto)).toMatch(uuid4Regex); } }); it('return valid uuid v4 even if crypto invoked causes an error', () => { - (global as any).crypto = { + const crypto = { getRandomValues: () => { throw new Error('yo'); }, @@ -339,7 +340,7 @@ describe('uuid4 generation', () => { }; for (let index = 0; index < 1_000; index++) { - expect(uuid4()).toMatch(uuid4Regex); + expect(uuid4(crypto)).toMatch(uuid4Regex); } }); @@ -355,10 +356,12 @@ describe('uuid4 generation', () => { } }; - (global as any).crypto = { getRandomValues }; + const crypto = { getRandomValues }; for (let index = 0; index < 1_000; index++) { - expect(uuid4()).toMatch(uuid4Regex); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - we are testing a corner case + expect(uuid4(crypto)).toMatch(uuid4Regex); } }); }); diff --git a/packages/core/test/utils-hoist/normalize-url.test.ts b/packages/core/test/utils-hoist/normalize-url.test.ts index 080905058e1d..0e8b5d787601 100644 --- a/packages/core/test/utils-hoist/normalize-url.test.ts +++ b/packages/core/test/utils-hoist/normalize-url.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { normalizeUrlToBase } from '../../src/utils-hoist/normalize'; describe('normalizeUrlToBase()', () => { diff --git a/packages/core/test/utils-hoist/normalize.test.ts b/packages/core/test/utils-hoist/normalize.test.ts index 985c0dbf8963..fb1cc75099e8 100644 --- a/packages/core/test/utils-hoist/normalize.test.ts +++ b/packages/core/test/utils-hoist/normalize.test.ts @@ -2,6 +2,7 @@ * @jest-environment jsdom */ +import { describe, expect, test, vi } from 'vitest'; import { addNonEnumerableProperty, normalize } from '../../src'; import * as isModule from '../../src/utils-hoist/is'; import * as stacktraceModule from '../../src/utils-hoist/stacktrace'; @@ -48,8 +49,8 @@ describe('normalize()', () => { }); }); - describe('extracts data from `Event` objects', () => { - const isElement = jest.spyOn(isModule, 'isElement').mockReturnValue(true); + test('extracts data from `Event` objects', () => { + const isElement = vi.spyOn(isModule, 'isElement').mockReturnValue(true); const getAttribute = () => undefined; const parkElement = { tagName: 'PARK', getAttribute }; @@ -594,7 +595,7 @@ describe('normalize()', () => { describe('handles serialization errors', () => { test('restricts effect of error to problematic node', () => { - jest.spyOn(stacktraceModule, 'getFunctionName').mockImplementationOnce(() => { + vi.spyOn(stacktraceModule, 'getFunctionName').mockImplementationOnce(() => { throw new Error('Nope'); }); diff --git a/packages/core/test/utils-hoist/object.test.ts b/packages/core/test/utils-hoist/object.test.ts index 6ccfd71f2218..668d071108f9 100644 --- a/packages/core/test/utils-hoist/object.test.ts +++ b/packages/core/test/utils-hoist/object.test.ts @@ -4,6 +4,7 @@ import type { WrappedFunction } from '../../src/types-hoist'; +import { describe, expect, it, test, vi } from 'vitest'; import { addNonEnumerableProperty, dropUndefinedKeys, @@ -22,7 +23,7 @@ describe('fill()', () => { }, }; const name = 'foo'; - const replacement = jest.fn().mockImplementationOnce(cb => cb); + const replacement = vi.fn().mockImplementationOnce(cb => cb); fill(source, name, replacement); @@ -43,7 +44,7 @@ describe('fill()', () => { expect(source.prop()).toEqual(41); - const replacement = jest.fn().mockImplementation(() => { + const replacement = vi.fn().mockImplementation(() => { return () => 42; }); fill(source, 'prop', replacement); @@ -58,7 +59,7 @@ describe('fill()', () => { foo: (): number => 42, }; const name = 'foo'; - const replacement = jest.fn().mockImplementationOnce(cb => { + const replacement = vi.fn().mockImplementationOnce(cb => { expect(cb).toBe(source.foo); return () => 1337; }); @@ -75,12 +76,12 @@ describe('fill()', () => { foo: (): number => 42, }; const name = 'foo'; - const replacement = jest.fn().mockImplementationOnce(cb => { + const replacement = vi.fn().mockImplementationOnce(cb => { expect(cb).toBe(source.foo); return () => 1337; }); - const replacement2 = jest.fn().mockImplementationOnce(cb => { + const replacement2 = vi.fn().mockImplementationOnce(cb => { expect(cb).toBe(source.foo); return () => 1338; }); @@ -374,7 +375,7 @@ describe('addNonEnumerableProperty', () => { }); it('works with a function', () => { - const func = jest.fn(); + const func = vi.fn(); addNonEnumerableProperty(func as any, 'foo', 'bar'); expect((func as any).foo).toBe('bar'); func(); @@ -409,8 +410,8 @@ describe('addNonEnumerableProperty', () => { describe('markFunctionWrapped', () => { it('works with a function', () => { - const originalFunc = jest.fn(); - const wrappedFunc = jest.fn(); + const originalFunc = vi.fn(); + const wrappedFunc = vi.fn(); markFunctionWrapped(wrappedFunc, originalFunc); expect((wrappedFunc as WrappedFunction).__sentry_original__).toBe(originalFunc); @@ -422,8 +423,8 @@ describe('markFunctionWrapped', () => { }); it('works with a frozen original function', () => { - const originalFunc = Object.freeze(jest.fn()); - const wrappedFunc = jest.fn(); + const originalFunc = Object.freeze(vi.fn()); + const wrappedFunc = vi.fn(); markFunctionWrapped(wrappedFunc, originalFunc); // cannot wrap because it is frozen, but we do not error! @@ -436,8 +437,8 @@ describe('markFunctionWrapped', () => { }); it('works with a frozen wrapped function', () => { - const originalFunc = Object.freeze(jest.fn()); - const wrappedFunc = Object.freeze(jest.fn()); + const originalFunc = Object.freeze(vi.fn()); + const wrappedFunc = Object.freeze(vi.fn()); markFunctionWrapped(wrappedFunc, originalFunc); // Skips adding the property, but also doesn't error diff --git a/packages/core/test/utils-hoist/path.test.ts b/packages/core/test/utils-hoist/path.test.ts index 64dd5caf20ca..6cd760ac84aa 100644 --- a/packages/core/test/utils-hoist/path.test.ts +++ b/packages/core/test/utils-hoist/path.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { basename, dirname } from '../../src/utils-hoist/path'; describe('path', () => { diff --git a/packages/core/test/utils-hoist/promisebuffer.test.ts b/packages/core/test/utils-hoist/promisebuffer.test.ts index 3357a06e820c..6b0b81d1588e 100644 --- a/packages/core/test/utils-hoist/promisebuffer.test.ts +++ b/packages/core/test/utils-hoist/promisebuffer.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test, vi } from 'vitest'; import { makePromiseBuffer } from '../../src/utils-hoist/promisebuffer'; import { SyncPromise } from '../../src/utils-hoist/syncpromise'; @@ -5,7 +6,7 @@ describe('PromiseBuffer', () => { describe('add()', () => { test('no limit', () => { const buffer = makePromiseBuffer(); - const p = jest.fn(() => new SyncPromise(resolve => setTimeout(resolve))); + const p = vi.fn(() => new SyncPromise(resolve => setTimeout(resolve))); void buffer.add(p); expect(buffer.$.length).toEqual(1); }); @@ -13,11 +14,11 @@ describe('PromiseBuffer', () => { test('with limit', () => { const buffer = makePromiseBuffer(1); let task1; - const producer1 = jest.fn(() => { + const producer1 = vi.fn(() => { task1 = new SyncPromise(resolve => setTimeout(resolve)); return task1; }); - const producer2 = jest.fn(() => new SyncPromise(resolve => setTimeout(resolve))); + const producer2 = vi.fn(() => new SyncPromise(resolve => setTimeout(resolve))); expect(buffer.add(producer1)).toEqual(task1); void expect(buffer.add(producer2)).rejects.toThrowError(); expect(buffer.$.length).toEqual(1); diff --git a/packages/core/test/utils-hoist/ratelimit.test.ts b/packages/core/test/utils-hoist/ratelimit.test.ts index 9a3f37bcf845..556280b93ba4 100644 --- a/packages/core/test/utils-hoist/ratelimit.test.ts +++ b/packages/core/test/utils-hoist/ratelimit.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import type { RateLimits } from '../../src/utils-hoist/ratelimit'; import { DEFAULT_RETRY_AFTER, diff --git a/packages/core/test/utils-hoist/severity.test.ts b/packages/core/test/utils-hoist/severity.test.ts index 65388428b65c..2f5595dfc9f0 100644 --- a/packages/core/test/utils-hoist/severity.test.ts +++ b/packages/core/test/utils-hoist/severity.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { severityLevelFromString } from '../../src/utils-hoist/severity'; describe('severityLevelFromString()', () => { diff --git a/packages/core/test/utils-hoist/stacktrace.test.ts b/packages/core/test/utils-hoist/stacktrace.test.ts index 1732c35d02bc..cfcc70819fa0 100644 --- a/packages/core/test/utils-hoist/stacktrace.test.ts +++ b/packages/core/test/utils-hoist/stacktrace.test.ts @@ -1,3 +1,4 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { nodeStackLineParser } from '../../src/utils-hoist/node-stack-trace'; import { stripSentryFramesAndReverse } from '../../src/utils-hoist/stacktrace'; @@ -119,7 +120,7 @@ describe('Stacktrace', () => { }); describe('node', () => { - const mockGetModule = jest.fn(); + const mockGetModule = vi.fn(); const parser = nodeStackLineParser(mockGetModule); const node = parser[1]; diff --git a/packages/core/test/utils-hoist/string.test.ts b/packages/core/test/utils-hoist/string.test.ts index bf959965cb45..364d29223a66 100644 --- a/packages/core/test/utils-hoist/string.test.ts +++ b/packages/core/test/utils-hoist/string.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { isMatchingPattern, stringMatchesSomePattern, truncate } from '../../src/utils-hoist/string'; describe('truncate()', () => { diff --git a/packages/core/test/utils-hoist/syncpromise.test.ts b/packages/core/test/utils-hoist/syncpromise.test.ts index 9dce11645b25..862f1e3c8a01 100644 --- a/packages/core/test/utils-hoist/syncpromise.test.ts +++ b/packages/core/test/utils-hoist/syncpromise.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test, vi } from 'vitest'; import { SyncPromise, rejectedSyncPromise, resolvedSyncPromise } from '../../src/utils-hoist/syncpromise'; describe('SyncPromise', () => { @@ -120,14 +121,14 @@ describe('SyncPromise', () => { }); test('with setTimeout', async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); expect.assertions(1); return new SyncPromise(resolve => { setTimeout(() => { resolve(12); }, 10); - jest.runAllTimers(); + vi.runAllTimers(); }).then(val => { expect(val).toEqual(12); }); @@ -146,7 +147,7 @@ describe('SyncPromise', () => { }); test('calling the callback not immediately', () => { - jest.useFakeTimers(); + vi.useFakeTimers(); expect.assertions(4); const qp = new SyncPromise(resolve => @@ -169,7 +170,7 @@ describe('SyncPromise', () => { .then(null, () => { // no-empty }); - jest.runAllTimers(); + vi.runAllTimers(); expect(qp).toHaveProperty('_value'); }); diff --git a/packages/core/test/utils-hoist/testutils.ts b/packages/core/test/utils-hoist/testutils.ts index 6130ee7ca365..b830095d3393 100644 --- a/packages/core/test/utils-hoist/testutils.ts +++ b/packages/core/test/utils-hoist/testutils.ts @@ -1,4 +1,7 @@ -export const testOnlyIfNodeVersionAtLeast = (minVersion: number): jest.It => { +import { it } from 'vitest'; + +// eslint-disable-next-line @typescript-eslint/ban-types +export const testOnlyIfNodeVersionAtLeast = (minVersion: number): Function => { const currentNodeVersion = process.env.NODE_VERSION; try { diff --git a/packages/core/test/utils-hoist/tracing.test.ts b/packages/core/test/utils-hoist/tracing.test.ts index 75b39a573437..851ee7b109c4 100644 --- a/packages/core/test/utils-hoist/tracing.test.ts +++ b/packages/core/test/utils-hoist/tracing.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, test } from 'vitest'; import { extractTraceparentData, propagationContextFromHeaders } from '../../src/utils-hoist/tracing'; const EXAMPLE_SENTRY_TRACE = '12312012123120121231201212312012-1121201211212012-1'; diff --git a/packages/core/test/utils-hoist/types/typedef.test.ts b/packages/core/test/utils-hoist/types/typedef.test.ts index 45719e13181c..fbf3ffcbb558 100644 --- a/packages/core/test/utils-hoist/types/typedef.test.ts +++ b/packages/core/test/utils-hoist/types/typedef.test.ts @@ -1,5 +1,6 @@ import * as fs from 'node:fs'; import * as path from 'node:path'; +import { test } from 'vitest'; const testStrings = ['/// ']; diff --git a/packages/core/test/utils-hoist/url.test.ts b/packages/core/test/utils-hoist/url.test.ts index a16c72dc1cd2..5126c2647bb7 100644 --- a/packages/core/test/utils-hoist/url.test.ts +++ b/packages/core/test/utils-hoist/url.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { getSanitizedUrlString, parseUrl, stripUrlQueryAndFragment } from '../../src/utils-hoist/url'; describe('stripQueryStringAndFragment', () => { diff --git a/packages/core/test/utils-hoist/vercelWaitUntil.test.ts b/packages/core/test/utils-hoist/vercelWaitUntil.test.ts index b8b6452cbac2..c35b5f076cd4 100644 --- a/packages/core/test/utils-hoist/vercelWaitUntil.test.ts +++ b/packages/core/test/utils-hoist/vercelWaitUntil.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it, vi } from 'vitest'; import { vercelWaitUntil } from '../../src/utils-hoist/vercelWaitUntil'; import { GLOBAL_OBJ } from '../../src/utils-hoist/worldwide'; @@ -27,7 +28,7 @@ describe('vercelWaitUntil', () => { }); it('should call waitUntil method if it is defined', () => { - const waitUntilMock = jest.fn(); + const waitUntilMock = vi.fn(); // @ts-expect-error - Not typed GLOBAL_OBJ[Symbol.for('@vercel/request-context')] = { get: () => ({ waitUntil: waitUntilMock }), diff --git a/packages/core/test/utils-hoist/worldwide.test.ts b/packages/core/test/utils-hoist/worldwide.test.ts index a3106e953861..efd8a152a0ab 100644 --- a/packages/core/test/utils-hoist/worldwide.test.ts +++ b/packages/core/test/utils-hoist/worldwide.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, test } from 'vitest'; import { GLOBAL_OBJ } from '../../src/utils-hoist/worldwide'; describe('GLOBAL_OBJ', () => { diff --git a/packages/core/tsconfig.test.json b/packages/core/tsconfig.test.json index 6fde53bec436..c2b522d04ee1 100644 --- a/packages/core/tsconfig.test.json +++ b/packages/core/tsconfig.test.json @@ -1,12 +1,12 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*"], + "include": ["test/**/*", "vite.config.ts"], "compilerOptions": { "lib": ["DOM", "ES2018"], // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node"] // other package-specific, test-specific options } diff --git a/packages/core/vite.config.ts b/packages/core/vite.config.ts new file mode 100644 index 000000000000..f18ec92095bc --- /dev/null +++ b/packages/core/vite.config.ts @@ -0,0 +1,8 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, + test: { + ...baseConfig.test, + }, +}; diff --git a/packages/deno/jest.config.js b/packages/deno/jest.config.js deleted file mode 100644 index 24f49ab59a4c..000000000000 --- a/packages/deno/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../jest/jest.config.js'); diff --git a/packages/deno/src/index.ts b/packages/deno/src/index.ts index d810b7429266..a906197b40c2 100644 --- a/packages/deno/src/index.ts +++ b/packages/deno/src/index.ts @@ -61,7 +61,9 @@ export { startSpanManual, startNewTrace, suppressTracing, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, linkedErrorsIntegration, functionToStringIntegration, requestDataIntegration, @@ -85,10 +87,7 @@ export { export { DenoClient } from './client'; -export { - getDefaultIntegrations, - init, -} from './sdk'; +export { getDefaultIntegrations, init } from './sdk'; export { denoContextIntegration } from './integrations/context'; export { globalHandlersIntegration } from './integrations/globalhandlers'; diff --git a/packages/deno/src/sdk.ts b/packages/deno/src/sdk.ts index 25dc550fc353..588d417f5ed9 100644 --- a/packages/deno/src/sdk.ts +++ b/packages/deno/src/sdk.ts @@ -24,6 +24,8 @@ export function getDefaultIntegrations(_options: Options): Integration[] { // We return a copy of the defaultIntegrations here to avoid mutating this return [ // Common + // TODO(v10): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration` + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration(), functionToStringIntegration(), linkedErrorsIntegration(), diff --git a/packages/eslint-plugin-sdk/test/lib/rules/no-eq-empty.test.ts b/packages/eslint-plugin-sdk/test/lib/rules/no-eq-empty.test.ts index 9a4c4c5a2ed6..08cfbf8f10e4 100644 --- a/packages/eslint-plugin-sdk/test/lib/rules/no-eq-empty.test.ts +++ b/packages/eslint-plugin-sdk/test/lib/rules/no-eq-empty.test.ts @@ -1,5 +1,5 @@ import { RuleTester } from 'eslint'; -import { describe } from 'vitest'; +import { describe, test } from 'vitest'; // @ts-expect-error untyped module import rule from '../../../src/rules/no-eq-empty'; diff --git a/packages/feedback/.eslintrc.js b/packages/feedback/.eslintrc.js index 0b547ffc828c..0c83f7d0ff9d 100644 --- a/packages/feedback/.eslintrc.js +++ b/packages/feedback/.eslintrc.js @@ -5,12 +5,4 @@ module.exports = { extends: ['../../.eslintrc.js'], - overrides: [ - { - files: ['jest.setup.ts', 'jest.config.ts'], - parserOptions: { - project: ['tsconfig.test.json'], - }, - }, - ], }; diff --git a/packages/feedback/jest.config.js b/packages/feedback/jest.config.js deleted file mode 100644 index cd02790794a7..000000000000 --- a/packages/feedback/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const baseConfig = require('../../jest/jest.config.js'); - -module.exports = { - ...baseConfig, - testEnvironment: 'jsdom', -}; diff --git a/packages/feedback/package.json b/packages/feedback/package.json index 45eccf3886a7..1a25a8dbcec8 100644 --- a/packages/feedback/package.json +++ b/packages/feedback/package.json @@ -62,8 +62,8 @@ "clean": "rimraf build sentry-internal-feedback-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "jest", - "test:watch": "jest --watch", + "test": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "volta": { diff --git a/packages/feedback/src/core/TestClient.ts b/packages/feedback/src/core/TestClient.ts index 1acfcb87d8e8..6ff745dcb4e4 100644 --- a/packages/feedback/src/core/TestClient.ts +++ b/packages/feedback/src/core/TestClient.ts @@ -14,6 +14,7 @@ export class TestClient extends Client { /** * */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any public eventFromException(exception: any): PromiseLike { return resolvedSyncPromise({ exception: { diff --git a/packages/feedback/src/modal/components/Dialog.tsx b/packages/feedback/src/modal/components/Dialog.tsx index 7bcde062a7c7..27fcc5991de0 100644 --- a/packages/feedback/src/modal/components/Dialog.tsx +++ b/packages/feedback/src/modal/components/Dialog.tsx @@ -1,5 +1,4 @@ import type { FeedbackFormData, FeedbackInternalOptions } from '@sentry/core'; -// biome-ignore lint/nursery/noUnusedImports: reason import { Fragment, h } from 'preact'; // eslint-disable-line @typescript-eslint/no-unused-vars import type { VNode } from 'preact'; import { useCallback, useMemo, useState } from 'preact/hooks'; diff --git a/packages/feedback/src/modal/components/DialogHeader.tsx b/packages/feedback/src/modal/components/DialogHeader.tsx index 3615bdf5068b..b282cc440ef6 100644 --- a/packages/feedback/src/modal/components/DialogHeader.tsx +++ b/packages/feedback/src/modal/components/DialogHeader.tsx @@ -1,5 +1,4 @@ import type { FeedbackInternalOptions } from '@sentry/core'; -// biome-ignore lint/nursery/noUnusedImports: reason import { h } from 'preact'; // eslint-disable-line @typescript-eslint/no-unused-vars import type { VNode } from 'preact'; import { useMemo } from 'preact/hooks'; diff --git a/packages/feedback/src/modal/components/Form.tsx b/packages/feedback/src/modal/components/Form.tsx index 3d508f895429..15b9b232591e 100644 --- a/packages/feedback/src/modal/components/Form.tsx +++ b/packages/feedback/src/modal/components/Form.tsx @@ -5,7 +5,6 @@ import type { FeedbackScreenshotIntegration, SendFeedback, } from '@sentry/core'; -// biome-ignore lint/nursery/noUnusedImports: reason import { h } from 'preact'; // eslint-disable-line @typescript-eslint/no-unused-vars import type { JSX, VNode } from 'preact'; import { useCallback, useState } from 'preact/hooks'; diff --git a/packages/feedback/src/screenshot/components/Crop.tsx b/packages/feedback/src/screenshot/components/Crop.tsx index e019d8c510e0..3b31ee71573c 100644 --- a/packages/feedback/src/screenshot/components/Crop.tsx +++ b/packages/feedback/src/screenshot/components/Crop.tsx @@ -51,7 +51,11 @@ interface FactoryParams { options: FeedbackInternalOptions; } -export default function CropFactory({ h, hooks, options }: FactoryParams): (props: { +export default function CropFactory({ + h, + hooks, + options, +}: FactoryParams): (props: { action: 'crop' | 'annotate' | ''; imageBuffer: HTMLCanvasElement; croppingRef: Hooks.Ref; diff --git a/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx b/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx index 9f49abf60e6f..9e8e708ec580 100644 --- a/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx +++ b/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx @@ -1,6 +1,5 @@ import type { FeedbackInternalOptions, FeedbackModalIntegration } from '@sentry/core'; import type { ComponentType, VNode, h as hType } from 'preact'; -// biome-ignore lint/nursery/noUnusedImports: reason import { h } from 'preact'; // eslint-disable-line @typescript-eslint/no-unused-vars import type * as Hooks from 'preact/hooks'; import { WINDOW } from '../../constants'; diff --git a/packages/feedback/src/core/components/Actor.test.ts b/packages/feedback/test/core/components/Actor.test.ts similarity index 83% rename from packages/feedback/src/core/components/Actor.test.ts rename to packages/feedback/test/core/components/Actor.test.ts index 5eee6709a065..06782f10393a 100644 --- a/packages/feedback/src/core/components/Actor.test.ts +++ b/packages/feedback/test/core/components/Actor.test.ts @@ -1,12 +1,17 @@ -import { TRIGGER_LABEL } from '../../constants'; -import { getFeedback } from '../getFeedback'; -import { buildFeedbackIntegration } from '../integration'; +/** + * @vitest-environment jsdom + */ +import { vi, describe, it, expect } from 'vitest'; + +import { TRIGGER_LABEL } from '../../../src/constants'; +import { getFeedback } from '../../../src/core/getFeedback'; +import { buildFeedbackIntegration } from '../../../src/core/integration'; import { mockSdk } from '../mockSdk'; describe('Actor', () => { it('renders the actor button', () => { const feedbackIntegration = buildFeedbackIntegration({ - lazyLoadIntegration: jest.fn(), + lazyLoadIntegration: vi.fn(), }); const configuredIntegration = feedbackIntegration({}); @@ -27,7 +32,7 @@ describe('Actor', () => { it('renders the correct aria label for the button', () => { const feedbackIntegration = buildFeedbackIntegration({ - lazyLoadIntegration: jest.fn(), + lazyLoadIntegration: vi.fn(), }); const configuredIntegration = feedbackIntegration({}); diff --git a/packages/feedback/src/core/getFeedback.test.ts b/packages/feedback/test/core/getFeedback.test.ts similarity index 83% rename from packages/feedback/src/core/getFeedback.test.ts rename to packages/feedback/test/core/getFeedback.test.ts index 35644a992006..ce5ad0444755 100644 --- a/packages/feedback/src/core/getFeedback.test.ts +++ b/packages/feedback/test/core/getFeedback.test.ts @@ -1,6 +1,8 @@ +import { vi, describe, it, expect, beforeEach } from 'vitest'; + import { getCurrentScope } from '@sentry/core'; -import { getFeedback } from './getFeedback'; -import { buildFeedbackIntegration } from './integration'; +import { getFeedback } from '../../src/core/getFeedback'; +import { buildFeedbackIntegration } from '../../src/core/integration'; import { mockSdk } from './mockSdk'; describe('getFeedback', () => { @@ -26,7 +28,7 @@ describe('getFeedback', () => { it('works with a client with Feedback', () => { const feedbackIntegration = buildFeedbackIntegration({ - lazyLoadIntegration: jest.fn(), + lazyLoadIntegration: vi.fn(), }); const configuredIntegration = feedbackIntegration({}); diff --git a/packages/feedback/src/core/mockSdk.ts b/packages/feedback/test/core/mockSdk.ts similarity index 84% rename from packages/feedback/src/core/mockSdk.ts rename to packages/feedback/test/core/mockSdk.ts index 70acb6e3aa12..0846aa4a7c02 100644 --- a/packages/feedback/src/core/mockSdk.ts +++ b/packages/feedback/test/core/mockSdk.ts @@ -1,7 +1,9 @@ +import { vi } from 'vitest'; + import type { Envelope, Transport, TransportMakeRequestResponse } from '@sentry/core'; -import type { TestClientOptions } from './TestClient'; -import { getDefaultClientOptions, init } from './TestClient'; +import type { TestClientOptions } from '../../src/core/TestClient'; +import { getDefaultClientOptions, init } from '../../src/core/TestClient'; export interface MockSdkParams { sentryOptions?: Partial; @@ -11,7 +13,7 @@ class MockTransport implements Transport { public send: (request: Envelope) => PromiseLike; public constructor() { - this.send = jest.fn(async () => { + this.send = vi.fn(async () => { return { statusCode: 200, }; diff --git a/packages/feedback/src/core/sendFeedback.test.ts b/packages/feedback/test/core/sendFeedback.test.ts similarity index 90% rename from packages/feedback/src/core/sendFeedback.test.ts rename to packages/feedback/test/core/sendFeedback.test.ts index 6f7e9c0050a9..37e552090782 100644 --- a/packages/feedback/src/core/sendFeedback.test.ts +++ b/packages/feedback/test/core/sendFeedback.test.ts @@ -1,3 +1,8 @@ +/** + * @vitest-environment jsdom + */ +import { vi, describe, it, expect, beforeEach, afterAll } from 'vitest'; + import { addBreadcrumb, getClient, @@ -9,7 +14,7 @@ import { } from '@sentry/core'; import { mockSdk } from './mockSdk'; -import { sendFeedback } from './sendFeedback'; +import { sendFeedback } from '../../src/core/sendFeedback'; import { TextDecoder, TextEncoder } from 'util'; const patchedEncoder = (!global.window.TextEncoder && (global.window.TextEncoder = TextEncoder)) || true; @@ -20,7 +25,7 @@ describe('sendFeedback', () => { beforeEach(() => { getIsolationScope().clear(); getCurrentScope().clear(); - jest.clearAllMocks(); + vi.clearAllMocks(); }); afterAll(() => { @@ -32,7 +37,7 @@ describe('sendFeedback', () => { it('sends feedback with minimal options', async () => { mockSdk(); - const mockTransport = jest.spyOn(getClient()!.getTransport()!, 'send'); + const mockTransport = vi.spyOn(getClient()!.getTransport()!, 'send'); const promise = sendFeedback({ message: 'mi', @@ -67,7 +72,7 @@ describe('sendFeedback', () => { feedback: { message: 'mi', source: 'api', - url: 'http://localhost/', + url: 'http://localhost:3000/', }, }, level: 'info', @@ -83,7 +88,7 @@ describe('sendFeedback', () => { it('sends feedback with full options', async () => { mockSdk(); - const mockTransport = jest.spyOn(getClient()!.getTransport()!, 'send'); + const mockTransport = vi.spyOn(getClient()!.getTransport()!, 'send'); const promise = sendFeedback({ name: 'doe', @@ -142,7 +147,7 @@ describe('sendFeedback', () => { it('applies active span data to feedback', async () => { mockSdk({ sentryOptions: { tracesSampleRate: 1 } }); - const mockTransport = jest.spyOn(getClient()!.getTransport()!, 'send'); + const mockTransport = vi.spyOn(getClient()!.getTransport()!, 'send'); await startSpan({ name: 'test span' }, () => { return sendFeedback({ @@ -181,7 +186,7 @@ describe('sendFeedback', () => { message: 'mi', name: 'doe', source: 'api', - url: 'http://localhost/', + url: 'http://localhost:3000/', }, }, level: 'info', @@ -197,7 +202,7 @@ describe('sendFeedback', () => { it('applies scope data to feedback', async () => { mockSdk({ sentryOptions: { tracesSampleRate: 1 } }); - const mockTransport = jest.spyOn(getClient()!.getTransport()!, 'send'); + const mockTransport = vi.spyOn(getClient()!.getTransport()!, 'send'); await withIsolationScope(isolationScope => { isolationScope.setTag('test-1', 'tag'); @@ -242,7 +247,7 @@ describe('sendFeedback', () => { message: 'mi', name: 'doe', source: 'api', - url: 'http://localhost/', + url: 'http://localhost:3000/', }, }, extra: { @@ -266,7 +271,7 @@ describe('sendFeedback', () => { it('handles 400 transport error', async () => { mockSdk(); - jest.spyOn(getClient()!.getTransport()!, 'send').mockImplementation(() => { + vi.spyOn(getClient()!.getTransport()!, 'send').mockImplementation(() => { return Promise.resolve({ statusCode: 400 }); }); @@ -283,7 +288,7 @@ describe('sendFeedback', () => { it('handles 0 transport error', async () => { mockSdk(); - jest.spyOn(getClient()!.getTransport()!, 'send').mockImplementation(() => { + vi.spyOn(getClient()!.getTransport()!, 'send').mockImplementation(() => { return Promise.resolve({ statusCode: 0 }); }); @@ -300,7 +305,7 @@ describe('sendFeedback', () => { it('handles 200 transport response', async () => { mockSdk(); - jest.spyOn(getClient()!.getTransport()!, 'send').mockImplementation(() => { + vi.spyOn(getClient()!.getTransport()!, 'send').mockImplementation(() => { return Promise.resolve({ statusCode: 200 }); }); @@ -314,10 +319,10 @@ describe('sendFeedback', () => { }); it('handles timeout', async () => { - jest.useFakeTimers(); + vi.useFakeTimers(); mockSdk(); - jest.spyOn(getClient()!.getTransport()!, 'send').mockImplementation(() => { + vi.spyOn(getClient()!.getTransport()!, 'send').mockImplementation(() => { return new Promise(resolve => setTimeout(resolve, 10_000)); }); @@ -327,16 +332,16 @@ describe('sendFeedback', () => { message: 'mi', }); - jest.advanceTimersByTime(5_000); + vi.advanceTimersByTime(5_000); await expect(promise).rejects.toMatch('Unable to determine if Feedback was correctly sent.'); - jest.useRealTimers(); + vi.useRealTimers(); }); it('sends attachments', async () => { mockSdk(); - const mockTransport = jest.spyOn(getClient()!.getTransport()!, 'send'); + const mockTransport = vi.spyOn(getClient()!.getTransport()!, 'send'); const attachment1 = new Uint8Array([1, 2, 3, 4, 5]); const attachment2 = new Uint8Array([6, 7, 8, 9]); @@ -395,7 +400,7 @@ describe('sendFeedback', () => { message: 'mi', name: 'doe', source: 'api', - url: 'http://localhost/', + url: 'http://localhost:3000/', }, }, level: 'info', diff --git a/packages/feedback/tsconfig.json b/packages/feedback/tsconfig.json index c1fc08578508..50734351f40b 100644 --- a/packages/feedback/tsconfig.json +++ b/packages/feedback/tsconfig.json @@ -13,5 +13,5 @@ "react-dom": ["./node_modules/preact/compat/"] } }, - "include": ["src/**/*.ts","src/**/*.tsx"] + "include": ["src/**/*.ts", "src/**/*.tsx"] } diff --git a/packages/feedback/tsconfig.test.json b/packages/feedback/tsconfig.test.json index ad87caa06c48..d4f2aaaf435e 100644 --- a/packages/feedback/tsconfig.test.json +++ b/packages/feedback/tsconfig.test.json @@ -1,10 +1,10 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*.ts", "jest.config.ts", "jest.setup.ts"], + "include": ["test/**/*.ts", "vite.config.ts"], "compilerOptions": { - "types": ["node", "jest"], + "types": ["node"], "esModuleInterop": true, "allowJs": true, "noImplicitAny": true, diff --git a/packages/feedback/vite.config.ts b/packages/feedback/vite.config.ts new file mode 100644 index 000000000000..a5523c61f601 --- /dev/null +++ b/packages/feedback/vite.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config'; + +import baseConfig from '../../vite/vite.config'; + +export default defineConfig({ + ...baseConfig, + test: { + ...baseConfig.test, + }, +}); diff --git a/packages/gatsby/.eslintrc.js b/packages/gatsby/.eslintrc.js index ee0716e6ec2a..1ce19816e6bf 100644 --- a/packages/gatsby/.eslintrc.js +++ b/packages/gatsby/.eslintrc.js @@ -7,7 +7,7 @@ module.exports = { jsx: true, }, // ignore these because they're not covered by a `tsconfig`, which makes eslint throw an error - ignorePatterns: ['gatsby-node.d.ts'], + ignorePatterns: ['gatsby-node.d.ts', 'setup/globalSetup.ts'], overrides: [ { files: ['scripts/**/*.ts'], diff --git a/packages/gatsby/jest.config.js b/packages/gatsby/jest.config.js deleted file mode 100644 index 3770aa5b0db2..000000000000 --- a/packages/gatsby/jest.config.js +++ /dev/null @@ -1,10 +0,0 @@ -const baseConfig = require('../../jest/jest.config.js'); - -module.exports = { - ...baseConfig, - testEnvironment: 'jsdom', - transform: { - '^.+\\.js$': 'ts-jest', - ...baseConfig.transform, - }, -}; diff --git a/packages/gatsby/package.json b/packages/gatsby/package.json index 41181a78f074..e946acd039be 100644 --- a/packages/gatsby/package.json +++ b/packages/gatsby/package.json @@ -75,8 +75,8 @@ "clean": "rimraf build coverage *.d.ts sentry-gatsby-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "yarn ts-node scripts/pretest.ts && yarn jest", - "test:watch": "yarn ts-node scripts/pretest.ts && yarn jest --watch", + "test": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "volta": { diff --git a/packages/gatsby/scripts/pretest.ts b/packages/gatsby/setup/globalSetup.ts similarity index 73% rename from packages/gatsby/scripts/pretest.ts rename to packages/gatsby/setup/globalSetup.ts index bc64e0478b2f..c0c948d57110 100644 --- a/packages/gatsby/scripts/pretest.ts +++ b/packages/gatsby/setup/globalSetup.ts @@ -11,4 +11,8 @@ function ensurePluginTypes(): void { } } -ensurePluginTypes(); +import type { GlobalSetupContext } from 'vitest/node'; + +export default function setup(_: GlobalSetupContext) { + ensurePluginTypes(); +} diff --git a/packages/gatsby/test/gatsby-node.test.ts b/packages/gatsby/test/gatsby-node.test.ts index 5ff15ecf034e..e9170e3a40ad 100644 --- a/packages/gatsby/test/gatsby-node.test.ts +++ b/packages/gatsby/test/gatsby-node.test.ts @@ -1,13 +1,31 @@ -import { sentryWebpackPlugin } from '@sentry/webpack-plugin'; -import { onCreateWebpackConfig } from '../gatsby-node'; +import { describe, afterAll, afterEach, beforeEach, beforeAll, vi, it, expect } from 'vitest'; + +vi.hoisted( + () => + void mock('@sentry/webpack-plugin', { + sentryWebpackPlugin: vi.fn().mockReturnValue({}), + }), +); + +// Need to override mock because `gatsby-node.js` loads `@sentry/webpack-plugin` as a CJS file. +async function mock(mockedUri: string, stub: any) { + const { Module } = await import('module'); + + // @ts-expect-error test + Module._load_original = Module._load; + // @ts-expect-error test + Module._load = (uri, parent) => { + if (uri === mockedUri) return stub; + // @ts-expect-error test + return Module._load_original(uri, parent); + }; +} -jest.mock('@sentry/webpack-plugin', () => ({ - sentryWebpackPlugin: jest.fn().mockReturnValue({ - apply: jest.fn(), - }), -})); +import { onCreateWebpackConfig } from '../gatsby-node'; describe('onCreateWebpackConfig', () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { sentryWebpackPlugin } = require('@sentry/webpack-plugin'); let originalNodeEnv: string | undefined; beforeAll(() => { @@ -20,15 +38,15 @@ describe('onCreateWebpackConfig', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('sets a webpack config', () => { const actions = { - setWebpackConfig: jest.fn(), + setWebpackConfig: vi.fn(), }; - const getConfig = jest.fn().mockReturnValue({ devtool: 'source-map' }); + const getConfig = vi.fn().mockReturnValue({ devtool: 'source-map' }); onCreateWebpackConfig({ actions, getConfig }, {}); @@ -38,10 +56,10 @@ describe('onCreateWebpackConfig', () => { it('does not set a webpack config if enableClientWebpackPlugin is false', () => { const actions = { - setWebpackConfig: jest.fn(), + setWebpackConfig: vi.fn(), }; - const getConfig = jest.fn().mockReturnValue({ devtool: 'source-map' }); + const getConfig = vi.fn().mockReturnValue({ devtool: 'source-map' }); onCreateWebpackConfig({ actions, getConfig }, { enableClientWebpackPlugin: false }); @@ -50,21 +68,21 @@ describe('onCreateWebpackConfig', () => { describe('delete source maps after upload', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); const actions = { - setWebpackConfig: jest.fn(), + setWebpackConfig: vi.fn(), }; - const getConfig = jest.fn(); + const getConfig = vi.fn(); it('sets sourceMapFilesToDeleteAfterUpload when provided in options', () => { const actions = { - setWebpackConfig: jest.fn(), + setWebpackConfig: vi.fn(), }; - const getConfig = jest.fn().mockReturnValue({ devtool: 'source-map' }); + const getConfig = vi.fn().mockReturnValue({ devtool: 'source-map' }); onCreateWebpackConfig({ actions, getConfig }, { deleteSourcemapsAfterUpload: true }); @@ -79,7 +97,7 @@ describe('onCreateWebpackConfig', () => { ); }); - test.each([ + it.each([ { name: 'without provided options: sets hidden source maps and deletes source maps', initialConfig: undefined, diff --git a/packages/gatsby/test/index.test.ts b/packages/gatsby/test/index.test.ts index 2ebc76e04a7f..c82f31c7f41d 100644 --- a/packages/gatsby/test/index.test.ts +++ b/packages/gatsby/test/index.test.ts @@ -1,3 +1,5 @@ +import { describe, it, expect } from 'vitest'; + import * as GatsbyIntegration from '../src/index'; describe('package', () => { diff --git a/packages/gatsby/test/sdk.test.ts b/packages/gatsby/test/sdk.test.ts index dd267ad5c2e1..465a06a05c0b 100644 --- a/packages/gatsby/test/sdk.test.ts +++ b/packages/gatsby/test/sdk.test.ts @@ -1,23 +1,26 @@ +import type { Mock } from 'vitest'; +import { describe, afterEach, expect, vi, test } from 'vitest'; + import { SDK_VERSION, init } from '@sentry/react'; import { init as gatsbyInit } from '../src/sdk'; -jest.mock('@sentry/react', () => { - const actual = jest.requireActual('@sentry/react'); +vi.mock('@sentry/react', async requiredActual => { + const actual = (await requiredActual()) as any; return { ...actual, - init: jest.fn().mockImplementation(actual.init), + init: vi.fn().mockImplementation(actual.init), }; }); -const reactInit = init as jest.Mock; +const reactInit = init as Mock; describe('Initialize React SDK', () => { afterEach(() => reactInit.mockReset()); test('Has correct SDK metadata', () => { gatsbyInit({}); - const calledWith = reactInit.mock.calls[0][0]; + const calledWith = reactInit.mock.calls[0]?.[0]; const sdkMetadata = calledWith._metadata.sdk; expect(sdkMetadata.name).toStrictEqual('sentry.javascript.gatsby'); expect(sdkMetadata.version).toBe(SDK_VERSION); @@ -31,7 +34,7 @@ describe('Initialize React SDK', () => { test('not defined by default', () => { gatsbyInit({}); expect(reactInit).toHaveBeenCalledTimes(1); - const callingObject = reactInit.mock.calls[0][0]; + const callingObject = reactInit.mock.calls[0]?.[0]; expect(callingObject.environment).not.toBeDefined(); }); @@ -40,7 +43,7 @@ describe('Initialize React SDK', () => { environment: 'custom env!', }); expect(reactInit).toHaveBeenCalledTimes(1); - const callingObject = reactInit.mock.calls[0][0]; + const callingObject = reactInit.mock.calls[0]?.[0]; expect(callingObject.environment).toStrictEqual('custom env!'); }); }); diff --git a/packages/gatsby/tsconfig.test.json b/packages/gatsby/tsconfig.test.json index 9d57a32062ed..cd46cfe5ccab 100644 --- a/packages/gatsby/tsconfig.test.json +++ b/packages/gatsby/tsconfig.test.json @@ -1,11 +1,11 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*"], + "include": ["test/**/*", "vite.config.ts", "setup/**/*"], "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"], + "types": ["node"], // other package-specific, test-specific options diff --git a/packages/gatsby/vite.config.ts b/packages/gatsby/vite.config.ts new file mode 100644 index 000000000000..43826c13a3bb --- /dev/null +++ b/packages/gatsby/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vitest/config'; + +import baseConfig from '../../vite/vite.config'; + +export default defineConfig({ + ...baseConfig, + test: { + ...baseConfig.test, + globalSetup: 'setup/globalSetup.ts', + }, +}); diff --git a/packages/google-cloud-serverless/src/index.ts b/packages/google-cloud-serverless/src/index.ts index 4d7c23ecf87e..9505ef6dd248 100644 --- a/packages/google-cloud-serverless/src/index.ts +++ b/packages/google-cloud-serverless/src/index.ts @@ -55,7 +55,9 @@ export { requestDataIntegration, fsIntegration, functionToStringIntegration, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, linkedErrorsIntegration, setMeasurement, getActiveSpan, diff --git a/packages/nestjs/package.json b/packages/nestjs/package.json index 19b11c02b75c..5cd0f56c3e2c 100644 --- a/packages/nestjs/package.json +++ b/packages/nestjs/package.json @@ -46,9 +46,9 @@ "dependencies": { "@opentelemetry/api": "^1.9.0", "@opentelemetry/core": "^1.30.1", - "@opentelemetry/instrumentation": "0.57.1", - "@opentelemetry/instrumentation-nestjs-core": "0.44.0", - "@opentelemetry/semantic-conventions": "^1.27.0", + "@opentelemetry/instrumentation": "0.57.2", + "@opentelemetry/instrumentation-nestjs-core": "0.44.1", + "@opentelemetry/semantic-conventions": "^1.30.0", "@sentry/core": "9.2.0", "@sentry/node": "9.2.0" }, diff --git a/packages/nestjs/test/integrations/nest.test.ts b/packages/nestjs/test/integrations/nest.test.ts index b7ad5c041616..f4b60dde05df 100644 --- a/packages/nestjs/test/integrations/nest.test.ts +++ b/packages/nestjs/test/integrations/nest.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { beforeEach, describe, expect, it, vi, afterEach } from 'vitest'; import * as core from '@sentry/core'; import { isPatched } from '../../src/integrations/helpers'; diff --git a/packages/nextjs/package.json b/packages/nextjs/package.json index 5f63ccc9ab36..a070fc80a331 100644 --- a/packages/nextjs/package.json +++ b/packages/nextjs/package.json @@ -77,7 +77,7 @@ }, "dependencies": { "@opentelemetry/api": "^1.9.0", - "@opentelemetry/semantic-conventions": "^1.28.0", + "@opentelemetry/semantic-conventions": "^1.30.0", "@rollup/plugin-commonjs": "28.0.1", "@sentry-internal/browser-utils": "9.2.0", "@sentry/core": "9.2.0", diff --git a/packages/nextjs/src/client/index.ts b/packages/nextjs/src/client/index.ts index c8e6d21837fd..9b1d58610e0d 100644 --- a/packages/nextjs/src/client/index.ts +++ b/packages/nextjs/src/client/index.ts @@ -51,7 +51,9 @@ export function init(options: BrowserOptions): Client | undefined { addEventProcessor(filterIncompleteNavigationTransactions); const filterNextRedirectError: EventProcessor = (event, hint) => - isRedirectNavigationError(hint?.originalException) ? null : event; + isRedirectNavigationError(hint?.originalException) || event.exception?.values?.[0]?.value === 'NEXT_REDIRECT' + ? null + : event; filterNextRedirectError.id = 'NextRedirectErrorFilter'; addEventProcessor(filterNextRedirectError); diff --git a/packages/nextjs/src/client/routing/appRouterRoutingInstrumentation.ts b/packages/nextjs/src/client/routing/appRouterRoutingInstrumentation.ts index 6ff2da7f9a37..312a7119c250 100644 --- a/packages/nextjs/src/client/routing/appRouterRoutingInstrumentation.ts +++ b/packages/nextjs/src/client/routing/appRouterRoutingInstrumentation.ts @@ -94,31 +94,32 @@ export function appRouterInstrumentNavigation(client: Client): void { // @ts-expect-error Weird type error related to not knowing how to associate return values with the individual functions - we can just ignore router[routerFunctionName] = new Proxy(router[routerFunctionName], { apply(target, thisArg, argArray) { - const span = startBrowserTracingNavigationSpan(client, { - name: INCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAME, - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.nextjs.app_router_instrumentation', - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', - }, - }); - - currentNavigationSpan = span; + let transactionName = INCOMPLETE_APP_ROUTER_INSTRUMENTATION_TRANSACTION_NAME; + const transactionAttributes: Record = { + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.nextjs.app_router_instrumentation', + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'url', + }; if (routerFunctionName === 'push') { - span?.updateName(transactionNameifyRouterArgument(argArray[0])); - span?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'url'); - span?.setAttribute('navigation.type', 'router.push'); + transactionName = transactionNameifyRouterArgument(argArray[0]); + transactionAttributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] = 'url'; + transactionAttributes['navigation.type'] = 'router.push'; } else if (routerFunctionName === 'replace') { - span?.updateName(transactionNameifyRouterArgument(argArray[0])); - span?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'url'); - span?.setAttribute('navigation.type', 'router.replace'); + transactionName = transactionNameifyRouterArgument(argArray[0]); + transactionAttributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] = 'url'; + transactionAttributes['navigation.type'] = 'router.replace'; } else if (routerFunctionName === 'back') { - span?.setAttribute('navigation.type', 'router.back'); + transactionAttributes['navigation.type'] = 'router.back'; } else if (routerFunctionName === 'forward') { - span?.setAttribute('navigation.type', 'router.forward'); + transactionAttributes['navigation.type'] = 'router.forward'; } + currentNavigationSpan = startBrowserTracingNavigationSpan(client, { + name: transactionName, + attributes: transactionAttributes, + }); + return target.apply(thisArg, argArray); }, }); diff --git a/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts b/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts index 810df8005c48..b08bdad5e9ab 100644 --- a/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts +++ b/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts @@ -5,9 +5,7 @@ interface FunctionComponent { } interface ClassComponent { - new ( - ...args: unknown[] - ): { + new (...args: unknown[]): { props?: unknown; render(...args: unknown[]): unknown; }; diff --git a/packages/nextjs/src/config/loaders/valueInjectionLoader.ts b/packages/nextjs/src/config/loaders/valueInjectionLoader.ts index 855700c13dd6..c15413cd1444 100644 --- a/packages/nextjs/src/config/loaders/valueInjectionLoader.ts +++ b/packages/nextjs/src/config/loaders/valueInjectionLoader.ts @@ -12,7 +12,6 @@ export type ValueInjectionLoaderOptions = { // This regex is shamelessly stolen from: https://github.com/getsentry/sentry-javascript-bundler-plugins/blob/7f984482c73e4284e8b12a08dfedf23b5a82f0af/packages/bundler-plugin-core/src/index.ts#L535-L539 const SKIP_COMMENT_AND_DIRECTIVE_REGEX = // Note: CodeQL complains that this regex potentially has n^2 runtime. This likely won't affect realistic files. - // biome-ignore lint/complexity/useRegexLiterals: No user input new RegExp('^(?:\\s*|/\\*(?:.|\\r|\\n)*?\\*/|//.*[\\n\\r])*(?:"[^"]*";?|\'[^\']*\';?)?'); /** diff --git a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts index f4223d752d47..fd01c150ec36 100644 --- a/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts +++ b/packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts @@ -4,7 +4,6 @@ import * as Sentry from '@sentry/nextjs'; // API) we use a shim if it doesn't exist. The logic for this is in the wrapping loader. import * as origModule from '__SENTRY_NEXTJS_REQUEST_ASYNC_STORAGE_SHIM__'; // @ts-expect-error We use `__SENTRY_WRAPPING_TARGET_FILE__` as a placeholder for the path to the file being wrapped. -// biome-ignore lint/nursery/noUnusedImports: Biome doesn't understand the shim with variable import path import * as serverComponentModule from '__SENTRY_WRAPPING_TARGET_FILE__'; import type { RequestAsyncStorage } from './requestAsyncStorageShim'; diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 5ff02da355a1..6c691424f115 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -57,6 +57,9 @@ export function constructWebpackConfigFunction( if (runtime !== 'client') { warnAboutDeprecatedConfigFiles(projectDir, runtime); } + if (runtime === 'server') { + warnAboutMissingonRequestErrorHandler(projectDir); + } let rawNewConfig = { ...incomingConfig }; @@ -435,6 +438,57 @@ async function addSentryToClientEntryProperty( return newEntryProperty; } +/** + * Make sure the instrumentation file has a `onRequestError` Handler + * + * @param projectDir The root directory of the project, where config files would be located + */ +function warnAboutMissingonRequestErrorHandler(projectDir: string): void { + const instrumentationPaths = [ + ['src', 'instrumentation.ts'], + ['src', 'instrumentation.js'], + ['instrumentation.ts'], + ['instrumentation.js'], + ]; + const instrumentationFile = instrumentationPaths + .map(pathSegments => path.resolve(projectDir, ...pathSegments)) + .find(function exists(filePath: string): string | null { + try { + fs.accessSync(filePath, fs.constants.F_OK); + return filePath; + } catch (error) { + return null; + } + }); + + function hasOnRequestErrorHandler(absolutePath: string): boolean { + try { + const content = fs.readFileSync(absolutePath, 'utf8'); + return content.includes('onRequestError'); + } catch (error) { + return false; + } + } + + if (!instrumentationFile) { + // eslint-disable-next-line no-console + return console.warn( + `${chalk.yellow( + '[@sentry/nextjs]', + )} Could not find a Next.js instrumentation file. This indicates an incomplete configuration of the Sentry SDK. An instrumentation file is required for the Sentry SDK to be initialized on the server: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#create-initialization-config-files`, + ); + } + + if (!hasOnRequestErrorHandler(instrumentationFile)) { + // eslint-disable-next-line no-console + console.warn( + `${chalk.yellow( + '[@sentry/nextjs]', + )} Could not find \`onRequestError\` hook in instrumentation file. This indicates outdated configuration of the Sentry SDK. Use \`Sentry.captureRequestError\` to instrument the \`onRequestError\` hook: https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/#errors-from-nested-react-server-components`, + ); + } +} + /** * Searches for old `sentry.(server|edge).config.ts` files and Next.js instrumentation hooks and warns if there are "old" * config files and no signs of them inside the instrumentation hook. diff --git a/packages/node/package.json b/packages/node/package.json index 73710ede6136..b450e96e5895 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -68,37 +68,37 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.30.1", "@opentelemetry/core": "^1.30.1", - "@opentelemetry/instrumentation": "^0.57.1", - "@opentelemetry/instrumentation-amqplib": "^0.46.0", - "@opentelemetry/instrumentation-connect": "0.43.0", - "@opentelemetry/instrumentation-dataloader": "0.16.0", - "@opentelemetry/instrumentation-express": "0.47.0", - "@opentelemetry/instrumentation-fastify": "0.44.1", - "@opentelemetry/instrumentation-fs": "0.19.0", - "@opentelemetry/instrumentation-generic-pool": "0.43.0", - "@opentelemetry/instrumentation-graphql": "0.47.0", - "@opentelemetry/instrumentation-hapi": "0.45.1", - "@opentelemetry/instrumentation-http": "0.57.1", - "@opentelemetry/instrumentation-ioredis": "0.47.0", - "@opentelemetry/instrumentation-kafkajs": "0.7.0", - "@opentelemetry/instrumentation-knex": "0.44.0", - "@opentelemetry/instrumentation-koa": "0.47.0", - "@opentelemetry/instrumentation-lru-memoizer": "0.44.0", - "@opentelemetry/instrumentation-mongodb": "0.51.0", - "@opentelemetry/instrumentation-mongoose": "0.46.0", - "@opentelemetry/instrumentation-mysql": "0.45.0", - "@opentelemetry/instrumentation-mysql2": "0.45.0", - "@opentelemetry/instrumentation-pg": "0.51.0", - "@opentelemetry/instrumentation-redis-4": "0.46.0", - "@opentelemetry/instrumentation-tedious": "0.18.0", - "@opentelemetry/instrumentation-undici": "0.10.0", + "@opentelemetry/instrumentation": "^0.57.2", + "@opentelemetry/instrumentation-amqplib": "^0.46.1", + "@opentelemetry/instrumentation-connect": "0.43.1", + "@opentelemetry/instrumentation-dataloader": "0.16.1", + "@opentelemetry/instrumentation-express": "0.47.1", + "@opentelemetry/instrumentation-fastify": "0.44.2", + "@opentelemetry/instrumentation-fs": "0.19.1", + "@opentelemetry/instrumentation-generic-pool": "0.43.1", + "@opentelemetry/instrumentation-graphql": "0.47.1", + "@opentelemetry/instrumentation-hapi": "0.45.2", + "@opentelemetry/instrumentation-http": "0.57.2", + "@opentelemetry/instrumentation-ioredis": "0.47.1", + "@opentelemetry/instrumentation-kafkajs": "0.7.1", + "@opentelemetry/instrumentation-knex": "0.44.1", + "@opentelemetry/instrumentation-koa": "0.47.1", + "@opentelemetry/instrumentation-lru-memoizer": "0.44.1", + "@opentelemetry/instrumentation-mongodb": "0.52.0", + "@opentelemetry/instrumentation-mongoose": "0.46.1", + "@opentelemetry/instrumentation-mysql": "0.45.1", + "@opentelemetry/instrumentation-mysql2": "0.45.2", + "@opentelemetry/instrumentation-pg": "0.51.1", + "@opentelemetry/instrumentation-redis-4": "0.46.1", + "@opentelemetry/instrumentation-tedious": "0.18.1", + "@opentelemetry/instrumentation-undici": "0.10.1", "@opentelemetry/resources": "^1.30.1", "@opentelemetry/sdk-trace-base": "^1.30.1", - "@opentelemetry/semantic-conventions": "^1.28.0", - "@prisma/instrumentation": "6.2.1", + "@opentelemetry/semantic-conventions": "^1.30.0", + "@prisma/instrumentation": "6.4.1", "@sentry/core": "9.2.0", "@sentry/opentelemetry": "9.2.0", - "import-in-the-middle": "^1.12.0" + "import-in-the-middle": "^1.13.0" }, "devDependencies": { "@types/node": "^18.19.1" @@ -119,9 +119,9 @@ "clean": "rimraf build coverage sentry-node-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "yarn test:jest", - "test:jest": "jest", - "test:watch": "jest --watch", + "test": "yarn test:unit", + "test:unit": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "volta": { diff --git a/packages/node/src/index.ts b/packages/node/src/index.ts index 2ebb7445a817..bdc8d6405217 100644 --- a/packages/node/src/index.ts +++ b/packages/node/src/index.ts @@ -75,7 +75,9 @@ export { withMonitor, requestDataIntegration, functionToStringIntegration, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, linkedErrorsIntegration, addEventProcessor, setContext, diff --git a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts index d645ac5c9ec2..4a268eaf31d5 100644 --- a/packages/node/src/integrations/http/SentryHttpInstrumentation.ts +++ b/packages/node/src/integrations/http/SentryHttpInstrumentation.ts @@ -471,7 +471,11 @@ export function recordRequestSession({ requestIsolationScope, response, sessionFlushingDelayMS, -}: { requestIsolationScope: Scope; response: EventEmitter; sessionFlushingDelayMS?: number }): void { +}: { + requestIsolationScope: Scope; + response: EventEmitter; + sessionFlushingDelayMS?: number; +}): void { requestIsolationScope.setSDKProcessingMetadata({ requestSession: { status: 'ok' }, }); diff --git a/packages/node/src/integrations/tracing/fastify.ts b/packages/node/src/integrations/tracing/fastify.ts index 3a509f8b97b6..2920b134d82d 100644 --- a/packages/node/src/integrations/tracing/fastify.ts +++ b/packages/node/src/integrations/tracing/fastify.ts @@ -38,6 +38,7 @@ const INTEGRATION_NAME = 'Fastify'; export const instrumentFastify = generateInstrumentOnce( INTEGRATION_NAME, () => + // eslint-disable-next-line deprecation/deprecation new FastifyInstrumentation({ requestHook(span) { addFastifySpanAttributes(span); diff --git a/packages/node/src/sdk/index.ts b/packages/node/src/sdk/index.ts index 7b9f98ed7461..710e00a3452b 100644 --- a/packages/node/src/sdk/index.ts +++ b/packages/node/src/sdk/index.ts @@ -51,6 +51,8 @@ function getCjsOnlyIntegrations(): Integration[] { export function getDefaultIntegrationsWithoutPerformance(): Integration[] { return [ // Common + // TODO(v10): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration` + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration(), functionToStringIntegration(), linkedErrorsIntegration(), diff --git a/packages/node/src/sdk/initOtel.ts b/packages/node/src/sdk/initOtel.ts index b50da334951d..27423a3d033a 100644 --- a/packages/node/src/sdk/initOtel.ts +++ b/packages/node/src/sdk/initOtel.ts @@ -17,8 +17,6 @@ import { SentryContextManager } from '../otel/contextManager'; import { isCjs } from '../utils/commonjs'; import type { NodeClient } from './client'; -declare const __IMPORT_META_URL_REPLACEMENT__: string; - // About 277h - this must fit into new Array(len)! const MAX_MAX_SPAN_WAIT_DURATION = 1_000_000; @@ -45,15 +43,11 @@ export function maybeInitializeEsmLoader(): void { // Register hook was added in v20.6.0 and v18.19.0 if (nodeMajor >= 22 || (nodeMajor === 20 && nodeMinor >= 6) || (nodeMajor === 18 && nodeMinor >= 19)) { - // We need to work around using import.meta.url directly because jest complains about it. - const importMetaUrl = - typeof __IMPORT_META_URL_REPLACEMENT__ !== 'undefined' ? __IMPORT_META_URL_REPLACEMENT__ : undefined; - - if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered && importMetaUrl) { + if (!GLOBAL_OBJ._sentryEsmLoaderHookRegistered) { try { const { addHookMessagePort } = createAddHookMessageChannel(); // @ts-expect-error register is available in these versions - moduleModule.register('import-in-the-middle/hook.mjs', importMetaUrl, { + moduleModule.register('import-in-the-middle/hook.mjs', import.meta.url, { data: { addHookMessagePort, include: [] }, transferList: [addHookMessagePort], }); diff --git a/packages/node/test/cron.test.ts b/packages/node/test/cron.test.ts index d068280a41e0..5ae016b43ac9 100644 --- a/packages/node/test/cron.test.ts +++ b/packages/node/test/cron.test.ts @@ -1,18 +1,19 @@ import * as SentryCore from '@sentry/core'; +import { type MockInstance, afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { cron } from '../src'; import type { CronJob, CronJobParams } from '../src/cron/cron'; import type { NodeCron, NodeCronOptions } from '../src/cron/node-cron'; describe('cron check-ins', () => { - let withMonitorSpy: jest.SpyInstance; + let withMonitorSpy: MockInstance; beforeEach(() => { - withMonitorSpy = jest.spyOn(SentryCore, 'withMonitor'); + withMonitorSpy = vi.spyOn(SentryCore, 'withMonitor'); }); afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); }); describe('cron', () => { @@ -48,43 +49,45 @@ describe('cron check-ins', () => { } } - test('new CronJob()', done => { - expect.assertions(4); - - const CronJobWithCheckIn = cron.instrumentCron(CronJobMock, 'my-cron-job'); - - new CronJobWithCheckIn( - '* * * Jan,Sep Sun', - () => { - expect(withMonitorSpy).toHaveBeenCalledTimes(1); - expect(withMonitorSpy).toHaveBeenLastCalledWith('my-cron-job', expect.anything(), { - schedule: { type: 'crontab', value: '* * * 1,9 0' }, - timezone: 'America/Los_Angeles', - }); - done(); - }, - undefined, - true, - 'America/Los_Angeles', - ); - }); + test('new CronJob()', () => + new Promise(done => { + expect.assertions(4); + + const CronJobWithCheckIn = cron.instrumentCron(CronJobMock, 'my-cron-job'); + + new CronJobWithCheckIn( + '* * * Jan,Sep Sun', + () => { + expect(withMonitorSpy).toHaveBeenCalledTimes(1); + expect(withMonitorSpy).toHaveBeenLastCalledWith('my-cron-job', expect.anything(), { + schedule: { type: 'crontab', value: '* * * 1,9 0' }, + timezone: 'America/Los_Angeles', + }); + done(); + }, + undefined, + true, + 'America/Los_Angeles', + ); + })); - test('CronJob.from()', done => { - expect.assertions(4); + test('CronJob.from()', () => + new Promise(done => { + expect.assertions(4); - const CronJobWithCheckIn = cron.instrumentCron(CronJobMock, 'my-cron-job'); + const CronJobWithCheckIn = cron.instrumentCron(CronJobMock, 'my-cron-job'); - CronJobWithCheckIn.from({ - cronTime: '* * * Jan,Sep Sun', - onTick: () => { - expect(withMonitorSpy).toHaveBeenCalledTimes(1); - expect(withMonitorSpy).toHaveBeenLastCalledWith('my-cron-job', expect.anything(), { - schedule: { type: 'crontab', value: '* * * 1,9 0' }, - }); - done(); - }, - }); - }); + CronJobWithCheckIn.from({ + cronTime: '* * * Jan,Sep Sun', + onTick: () => { + expect(withMonitorSpy).toHaveBeenCalledTimes(1); + expect(withMonitorSpy).toHaveBeenLastCalledWith('my-cron-job', expect.anything(), { + schedule: { type: 'crontab', value: '* * * 1,9 0' }, + }); + done(); + }, + }); + })); test('throws with multiple jobs same name', () => { const CronJobWithCheckIn = cron.instrumentCron(CronJobMock, 'my-cron-job'); @@ -108,32 +111,33 @@ describe('cron check-ins', () => { }); describe('node-cron', () => { - test('calls withMonitor', done => { - expect.assertions(5); - - const nodeCron: NodeCron = { - schedule: (expression: string, callback: () => void, options?: NodeCronOptions): unknown => { - expect(expression).toBe('* * * Jan,Sep Sun'); - expect(callback).toBeInstanceOf(Function); - expect(options?.name).toBe('my-cron-job'); - return callback(); - }, - }; - - const cronWithCheckIn = cron.instrumentNodeCron(nodeCron); - - cronWithCheckIn.schedule( - '* * * Jan,Sep Sun', - () => { - expect(withMonitorSpy).toHaveBeenCalledTimes(1); - expect(withMonitorSpy).toHaveBeenLastCalledWith('my-cron-job', expect.anything(), { - schedule: { type: 'crontab', value: '* * * 1,9 0' }, - }); - done(); - }, - { name: 'my-cron-job' }, - ); - }); + test('calls withMonitor', () => + new Promise(done => { + expect.assertions(5); + + const nodeCron: NodeCron = { + schedule: (expression: string, callback: () => void, options?: NodeCronOptions): unknown => { + expect(expression).toBe('* * * Jan,Sep Sun'); + expect(callback).toBeInstanceOf(Function); + expect(options?.name).toBe('my-cron-job'); + return callback(); + }, + }; + + const cronWithCheckIn = cron.instrumentNodeCron(nodeCron); + + cronWithCheckIn.schedule( + '* * * Jan,Sep Sun', + () => { + expect(withMonitorSpy).toHaveBeenCalledTimes(1); + expect(withMonitorSpy).toHaveBeenLastCalledWith('my-cron-job', expect.anything(), { + schedule: { type: 'crontab', value: '* * * 1,9 0' }, + }); + done(); + }, + { name: 'my-cron-job' }, + ); + })); test('throws without supplied name', () => { const nodeCron: NodeCron = { @@ -154,32 +158,33 @@ describe('cron check-ins', () => { }); describe('node-schedule', () => { - test('calls withMonitor', done => { - expect.assertions(5); - - class NodeScheduleMock { - scheduleJob( - nameOrExpression: string | Date | object, - expressionOrCallback: string | Date | object | (() => void), - callback: () => void, - ): unknown { - expect(nameOrExpression).toBe('my-cron-job'); - expect(expressionOrCallback).toBe('* * * Jan,Sep Sun'); - expect(callback).toBeInstanceOf(Function); - return callback(); + test('calls withMonitor', () => + new Promise(done => { + expect.assertions(5); + + class NodeScheduleMock { + scheduleJob( + nameOrExpression: string | Date | object, + expressionOrCallback: string | Date | object | (() => void), + callback: () => void, + ): unknown { + expect(nameOrExpression).toBe('my-cron-job'); + expect(expressionOrCallback).toBe('* * * Jan,Sep Sun'); + expect(callback).toBeInstanceOf(Function); + return callback(); + } } - } - const scheduleWithCheckIn = cron.instrumentNodeSchedule(new NodeScheduleMock()); + const scheduleWithCheckIn = cron.instrumentNodeSchedule(new NodeScheduleMock()); - scheduleWithCheckIn.scheduleJob('my-cron-job', '* * * Jan,Sep Sun', () => { - expect(withMonitorSpy).toHaveBeenCalledTimes(1); - expect(withMonitorSpy).toHaveBeenLastCalledWith('my-cron-job', expect.anything(), { - schedule: { type: 'crontab', value: '* * * 1,9 0' }, + scheduleWithCheckIn.scheduleJob('my-cron-job', '* * * Jan,Sep Sun', () => { + expect(withMonitorSpy).toHaveBeenCalledTimes(1); + expect(withMonitorSpy).toHaveBeenLastCalledWith('my-cron-job', expect.anything(), { + schedule: { type: 'crontab', value: '* * * 1,9 0' }, + }); + done(); }); - done(); - }); - }); + })); test('throws without crontab string', () => { class NodeScheduleMock { diff --git a/packages/node/test/helpers/conditional.ts b/packages/node/test/helpers/conditional.ts index 01e64d8804df..48df7d821da8 100644 --- a/packages/node/test/helpers/conditional.ts +++ b/packages/node/test/helpers/conditional.ts @@ -1,4 +1,5 @@ import { parseSemver } from '@sentry/core'; +import { it, test } from 'vitest'; const NODE_VERSION = parseSemver(process.versions.node).major; @@ -8,7 +9,7 @@ const NODE_VERSION = parseSemver(process.versions.node).major; * @param {{ min?: number; max?: number }} allowedVersion * @return {*} {jest.Describe} */ -export const conditionalTest = (allowedVersion: { min?: number; max?: number }): jest.It => { +export const conditionalTest = (allowedVersion: { min?: number; max?: number }) => { if (!NODE_VERSION) { return it.skip; } diff --git a/packages/node/test/integration/breadcrumbs.test.ts b/packages/node/test/integration/breadcrumbs.test.ts index d32d1490be76..20e19a8a4b55 100644 --- a/packages/node/test/integration/breadcrumbs.test.ts +++ b/packages/node/test/integration/breadcrumbs.test.ts @@ -3,10 +3,11 @@ import { startSpan } from '@sentry/opentelemetry'; import { getClient } from '../../src/'; import type { NodeClient } from '../../src/sdk/client'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { cleanupOtel, mockSdkInit } from '../helpers/mockSdkInit'; describe('Integration | breadcrumbs', () => { - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); afterEach(() => { cleanupOtel(); @@ -14,8 +15,8 @@ describe('Integration | breadcrumbs', () => { describe('without tracing', () => { it('correctly adds & retrieves breadcrumbs', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb }); @@ -50,8 +51,8 @@ describe('Integration | breadcrumbs', () => { }); it('handles parallel scopes', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb }); @@ -96,8 +97,8 @@ describe('Integration | breadcrumbs', () => { }); it('correctly adds & retrieves breadcrumbs', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb, beforeSendTransaction, tracesSampleRate: 1 }); @@ -141,8 +142,8 @@ describe('Integration | breadcrumbs', () => { }); it('correctly adds & retrieves breadcrumbs for the current isolation span only', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb, beforeSendTransaction, tracesSampleRate: 1 }); @@ -193,8 +194,8 @@ describe('Integration | breadcrumbs', () => { }); it('ignores scopes inside of root span', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb, beforeSendTransaction, tracesSampleRate: 1 }); @@ -234,8 +235,8 @@ describe('Integration | breadcrumbs', () => { }); it('handles deep nesting of scopes', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb, beforeSendTransaction, tracesSampleRate: 1 }); @@ -292,8 +293,8 @@ describe('Integration | breadcrumbs', () => { }); it('correctly adds & retrieves breadcrumbs in async spans', async () => { - const beforeSend = jest.fn(() => null); - const beforeBreadcrumb = jest.fn(breadcrumb => breadcrumb); + const beforeSend = vi.fn(() => null); + const beforeBreadcrumb = vi.fn(breadcrumb => breadcrumb); mockSdkInit({ beforeSend, beforeBreadcrumb, beforeSendTransaction, tracesSampleRate: 1 }); diff --git a/packages/node/test/integration/console.test.ts b/packages/node/test/integration/console.test.ts index 9d4d5a415ee5..401dffd34819 100644 --- a/packages/node/test/integration/console.test.ts +++ b/packages/node/test/integration/console.test.ts @@ -1,17 +1,18 @@ import * as SentryCore from '@sentry/core'; import { resetInstrumentationHandlers } from '@sentry/core'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { getClient } from '../../src'; import type { NodeClient } from '../../src'; import { consoleIntegration } from '../../src/integrations/console'; -const addBreadcrumbSpy = jest.spyOn(SentryCore, 'addBreadcrumb'); +const addBreadcrumbSpy = vi.spyOn(SentryCore, 'addBreadcrumb'); -jest.spyOn(console, 'log').mockImplementation(() => { +vi.spyOn(console, 'log').mockImplementation(() => { // noop so that we don't spam the logs }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); resetInstrumentationHandlers(); }); diff --git a/packages/node/test/integration/scope.test.ts b/packages/node/test/integration/scope.test.ts index 4ec51b813229..a3c8de869df5 100644 --- a/packages/node/test/integration/scope.test.ts +++ b/packages/node/test/integration/scope.test.ts @@ -2,6 +2,7 @@ import { getCapturedScopesOnSpan, getCurrentScope } from '@sentry/core'; import { getClient } from '@sentry/opentelemetry'; import { clearGlobalScope } from '../../../core/test/lib/clear-global-scope'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import * as Sentry from '../../src/'; import type { NodeClient } from '../../src/sdk/client'; import { cleanupOtel, mockSdkInit, resetGlobals } from '../helpers/mockSdkInit'; @@ -16,8 +17,8 @@ describe('Integration | Scope', () => { ['without tracing', false], ])('%s', (_name, tracingEnabled) => { it('correctly syncs OTEL context & Sentry hub/scope', async () => { - const beforeSend = jest.fn(() => null); - const beforeSendTransaction = jest.fn(() => null); + const beforeSend = vi.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); mockSdkInit({ tracesSampleRate: tracingEnabled ? 1 : 0, beforeSend, beforeSendTransaction }); @@ -130,8 +131,8 @@ describe('Integration | Scope', () => { }); it('isolates parallel root scopes', async () => { - const beforeSend = jest.fn(() => null); - const beforeSendTransaction = jest.fn(() => null); + const beforeSend = vi.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); mockSdkInit({ tracesSampleRate: tracingEnabled ? 1 : 0, beforeSend, beforeSendTransaction }); @@ -265,7 +266,7 @@ describe('Integration | Scope', () => { }); it('is applied to events', async () => { - const beforeSend = jest.fn(); + const beforeSend = vi.fn(); mockSdkInit({ beforeSend }); const client = Sentry.getClient(); @@ -323,7 +324,7 @@ describe('Integration | Scope', () => { }); it('is applied to events', async () => { - const beforeSend = jest.fn(); + const beforeSend = vi.fn(); mockSdkInit({ beforeSend }); const client = Sentry.getClient(); @@ -353,7 +354,7 @@ describe('Integration | Scope', () => { }); it('withIsolationScope works', async () => { - const beforeSend = jest.fn(); + const beforeSend = vi.fn(); mockSdkInit({ beforeSend }); const client = Sentry.getClient(); @@ -403,7 +404,7 @@ describe('Integration | Scope', () => { }); it('can be deeply nested', async () => { - const beforeSend = jest.fn(); + const beforeSend = vi.fn(); mockSdkInit({ beforeSend }); const client = Sentry.getClient(); @@ -475,7 +476,7 @@ describe('Integration | Scope', () => { }); it('is applied to events', async () => { - const beforeSend = jest.fn(); + const beforeSend = vi.fn(); mockSdkInit({ beforeSend }); const client = Sentry.getClient(); @@ -505,7 +506,7 @@ describe('Integration | Scope', () => { }); it('withScope works', async () => { - const beforeSend = jest.fn(); + const beforeSend = vi.fn(); mockSdkInit({ beforeSend }); const client = Sentry.getClient(); @@ -554,7 +555,7 @@ describe('Integration | Scope', () => { }); it('can be deeply nested', async () => { - const beforeSend = jest.fn(); + const beforeSend = vi.fn(); mockSdkInit({ beforeSend }); const client = Sentry.getClient(); @@ -600,7 +601,7 @@ describe('Integration | Scope', () => { }); it('automatically forks with OTEL context', async () => { - const beforeSend = jest.fn(); + const beforeSend = vi.fn(); mockSdkInit({ beforeSend }); const client = Sentry.getClient(); @@ -649,7 +650,7 @@ describe('Integration | Scope', () => { }); it('merges data from global, isolation and current scope', async () => { - const beforeSend = jest.fn(); + const beforeSend = vi.fn(); mockSdkInit({ beforeSend }); const client = Sentry.getClient(); diff --git a/packages/node/test/integration/transactions.test.ts b/packages/node/test/integration/transactions.test.ts index 1f396b11c3af..a7207d30cbac 100644 --- a/packages/node/test/integration/transactions.test.ts +++ b/packages/node/test/integration/transactions.test.ts @@ -5,18 +5,19 @@ import { logger } from '@sentry/core'; import type { TransactionEvent } from '@sentry/core'; import { SentrySpanProcessor } from '@sentry/opentelemetry'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import * as Sentry from '../../src'; import { cleanupOtel, getProvider, mockSdkInit } from '../helpers/mockSdkInit'; describe('Integration | Transactions', () => { afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); cleanupOtel(); }); it('correctly creates transaction & spans', async () => { const transactions: TransactionEvent[] = []; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transactions.push(event); return null; }); @@ -162,7 +163,7 @@ describe('Integration | Transactions', () => { }); it('correctly creates concurrent transaction & spans', async () => { - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); mockSdkInit({ tracesSampleRate: 1, beforeSendTransaction }); @@ -307,7 +308,7 @@ describe('Integration | Transactions', () => { }); it('correctly creates concurrent transaction & spans when using native OTEL tracer', async () => { - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); mockSdkInit({ tracesSampleRate: 1, beforeSendTransaction }); @@ -444,7 +445,7 @@ describe('Integration | Transactions', () => { }); it('correctly creates transaction & spans with a trace header data', async () => { - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); const traceId = 'd4cda95b652f4a1592b449d5929fda1b'; const parentSpanId = '6e0c63257de34c92'; @@ -552,14 +553,14 @@ describe('Integration | Transactions', () => { }); it('cleans up spans that are not flushed for over 5 mins', async () => { - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); const now = Date.now(); - jest.useFakeTimers(); - jest.setSystemTime(now); + vi.useFakeTimers(); + vi.setSystemTime(now); const logs: unknown[] = []; - jest.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg)); + vi.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg)); mockSdkInit({ tracesSampleRate: 1, beforeSendTransaction }); @@ -585,7 +586,7 @@ describe('Integration | Transactions', () => { await new Promise(resolve => setTimeout(resolve, 10 * 60 * 1000)); }); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); // Child-spans have been added to the exporter, but they are pending since they are waiting for their parent const finishedSpans1 = []; @@ -598,12 +599,12 @@ describe('Integration | Transactions', () => { expect(beforeSendTransaction).toHaveBeenCalledTimes(0); // Now wait for 5 mins - jest.advanceTimersByTime(5 * 60 * 1_000 + 1); + vi.advanceTimersByTime(5 * 60 * 1_000 + 1); // Adding another span will trigger the cleanup Sentry.startSpan({ name: 'other span' }, () => {}); - jest.advanceTimersByTime(1); + vi.advanceTimersByTime(1); // Old spans have been cleared away const finishedSpans2 = []; @@ -627,17 +628,17 @@ describe('Integration | Transactions', () => { it('allows to configure `maxSpanWaitDuration` to capture long running spans', async () => { const transactions: TransactionEvent[] = []; - const beforeSendTransaction = jest.fn(event => { + const beforeSendTransaction = vi.fn(event => { transactions.push(event); return null; }); const now = Date.now(); - jest.useFakeTimers(); - jest.setSystemTime(now); + vi.useFakeTimers(); + vi.setSystemTime(now); const logs: unknown[] = []; - jest.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg)); + vi.spyOn(logger, 'log').mockImplementation(msg => logs.push(msg)); mockSdkInit({ tracesSampleRate: 1, @@ -669,7 +670,7 @@ describe('Integration | Transactions', () => { }); // Now wait for 100 mins - jest.advanceTimersByTime(100 * 60 * 1_000); + vi.advanceTimersByTime(100 * 60 * 1_000); expect(beforeSendTransaction).toHaveBeenCalledTimes(1); expect(transactions).toHaveLength(1); diff --git a/packages/node/test/integrations/context.test.ts b/packages/node/test/integrations/context.test.ts index dde182c552ba..cad71c5af728 100644 --- a/packages/node/test/integrations/context.test.ts +++ b/packages/node/test/integrations/context.test.ts @@ -1,12 +1,22 @@ import * as os from 'node:os'; +import { afterAll, describe, expect, it, vi } from 'vitest'; import { getAppContext, getDeviceContext } from '../../src/integrations/context'; import { conditionalTest } from '../helpers/conditional'; +vi.mock('node:os', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const original = (await vi.importActual('node:os')) as typeof import('node:os'); + return { + ...original, + uptime: original.uptime, + }; +}); + describe('Context', () => { describe('getAppContext', () => { afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); conditionalTest({ max: 18 })('it does not return free_memory on older node versions', () => { @@ -23,7 +33,7 @@ describe('Context', () => { ); conditionalTest({ min: 22 })('returns no free_memory if process.availableMemory ', () => { - jest.spyOn(process as any, 'availableMemory').mockReturnValue(undefined as unknown as number); + vi.spyOn(process as any, 'availableMemory').mockReturnValue(undefined as unknown as number); const appContext = getAppContext(); expect(appContext.free_memory).toBeUndefined(); }); @@ -31,7 +41,7 @@ describe('Context', () => { describe('getDeviceContext', () => { afterAll(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('returns boot time if os.uptime is defined and returns a valid uptime', () => { @@ -40,7 +50,7 @@ describe('Context', () => { }); it('returns no boot time if os.uptime() returns undefined', () => { - jest.spyOn(os, 'uptime').mockReturnValue(undefined as unknown as number); + vi.spyOn(os, 'uptime').mockReturnValue(undefined as unknown as number); const deviceCtx = getDeviceContext({}); expect(deviceCtx.boot_time).toBeUndefined(); }); diff --git a/packages/node/test/integrations/contextlines.test.ts b/packages/node/test/integrations/contextlines.test.ts index 47827d8cfeb2..be074030b1a0 100644 --- a/packages/node/test/integrations/contextlines.test.ts +++ b/packages/node/test/integrations/contextlines.test.ts @@ -2,6 +2,7 @@ import * as fs from 'node:fs'; import { parseStackFrames } from '@sentry/core'; import type { StackFrame } from '@sentry/core'; +import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { MAX_CONTEXTLINES_COLNO, MAX_CONTEXTLINES_LINENO, @@ -11,6 +12,15 @@ import { import { defaultStackParser } from '../../src/sdk/api'; import { getError } from '../helpers/error'; +vi.mock('node:fs', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const original = (await vi.importActual('node:fs')) as typeof import('node:fs'); + return { + ...original, + createReadStream: original.createReadStream, + }; +}); + describe('ContextLines', () => { let contextLines: ReturnType; @@ -24,7 +34,7 @@ describe('ContextLines', () => { }); afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('limits', () => { @@ -39,7 +49,7 @@ describe('ContextLines', () => { }, ]; - const readStreamSpy = jest.spyOn(fs, 'createReadStream'); + const readStreamSpy = vi.spyOn(fs, 'createReadStream'); await addContext(frames); expect(readStreamSpy).not.toHaveBeenCalled(); }); @@ -55,7 +65,7 @@ describe('ContextLines', () => { }, ]; - const readStreamSpy = jest.spyOn(fs, 'createReadStream'); + const readStreamSpy = vi.spyOn(fs, 'createReadStream'); await addContext(frames); expect(readStreamSpy).not.toHaveBeenCalled(); }); @@ -73,7 +83,7 @@ describe('ContextLines', () => { }, ]; - const readStreamSpy = jest.spyOn(fs, 'createReadStream'); + const readStreamSpy = vi.spyOn(fs, 'createReadStream'); await addContext(frames); expect(frames[0]!.pre_context).toBeUndefined(); @@ -85,7 +95,7 @@ describe('ContextLines', () => { expect.assertions(1); const frames = parseStackFrames(defaultStackParser, new Error('test')); - const readStreamSpy = jest.spyOn(fs, 'createReadStream'); + const readStreamSpy = vi.spyOn(fs, 'createReadStream'); await addContext(frames); const numCalls = readStreamSpy.mock.calls.length; @@ -99,7 +109,7 @@ describe('ContextLines', () => { test('parseStack with ESM module names', async () => { expect.assertions(1); - const readStreamSpy = jest.spyOn(fs, 'createReadStream'); + const readStreamSpy = vi.spyOn(fs, 'createReadStream'); const framesWithFilePath: StackFrame[] = [ { colno: 1, @@ -116,7 +126,7 @@ describe('ContextLines', () => { test('parseStack with adding different file', async () => { expect.assertions(1); const frames = parseStackFrames(defaultStackParser, new Error('test')); - const readStreamSpy = jest.spyOn(fs, 'createReadStream'); + const readStreamSpy = vi.spyOn(fs, 'createReadStream'); await addContext(frames); @@ -170,7 +180,7 @@ describe('ContextLines', () => { test('parseStack with duplicate files', async () => { expect.assertions(1); - const readStreamSpy = jest.spyOn(fs, 'createReadStream'); + const readStreamSpy = vi.spyOn(fs, 'createReadStream'); const framesWithDuplicateFiles: StackFrame[] = [ { colno: 1, @@ -198,7 +208,7 @@ describe('ContextLines', () => { test('stack errors without lineno', async () => { expect.assertions(1); - const readStreamSpy = jest.spyOn(fs, 'createReadStream'); + const readStreamSpy = vi.spyOn(fs, 'createReadStream'); const framesWithDuplicateFiles: StackFrame[] = [ { colno: 1, @@ -215,7 +225,7 @@ describe('ContextLines', () => { test('parseStack with no context', async () => { expect.assertions(1); contextLines = _contextLinesIntegration({ frameContextLines: 0 }); - const readStreamSpy = jest.spyOn(fs, 'createReadStream'); + const readStreamSpy = vi.spyOn(fs, 'createReadStream'); const frames = parseStackFrames(defaultStackParser, new Error('test')); diff --git a/packages/node/test/integrations/http.test.ts b/packages/node/test/integrations/http.test.ts index a6a8e612c019..ad59c1709646 100644 --- a/packages/node/test/integrations/http.test.ts +++ b/packages/node/test/integrations/http.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { _shouldInstrumentSpans } from '../../src/integrations/http'; describe('httpIntegration', () => { diff --git a/packages/node/test/integrations/localvariables.test.ts b/packages/node/test/integrations/localvariables.test.ts index ad9f6804d9a3..72096fc4fa14 100644 --- a/packages/node/test/integrations/localvariables.test.ts +++ b/packages/node/test/integrations/localvariables.test.ts @@ -1,97 +1,102 @@ +import { describe, expect, it, vi } from 'vitest'; import { createRateLimiter } from '../../src/integrations/local-variables/common'; import { createCallbackList } from '../../src/integrations/local-variables/local-variables-sync'; import { NODE_MAJOR } from '../../src/nodeVersion'; -jest.useFakeTimers(); +vi.useFakeTimers(); const describeIf = (condition: boolean) => (condition ? describe : describe.skip); describeIf(NODE_MAJOR >= 18)('LocalVariables', () => { describe('createCallbackList', () => { - it('Should call callbacks in reverse order', done => { - const log: number[] = []; - - const { add, next } = createCallbackList(n => { - expect(log).toEqual([5, 4, 3, 2, 1]); - expect(n).toBe(15); - done(); - }); - - add(n => { - log.push(1); - next(n + 1); - }); - - add(n => { - log.push(2); - next(n + 1); - }); - - add(n => { - log.push(3); - next(n + 1); - }); - - add(n => { - log.push(4); - next(n + 1); - }); - - add(n => { - log.push(5); - next(n + 11); - }); - - next(0); - }); + it('Should call callbacks in reverse order', () => + new Promise(done => { + const log: number[] = []; - it('only calls complete once even if multiple next', done => { - const { add, next } = createCallbackList(n => { - expect(n).toBe(1); - done(); - }); + const { add, next } = createCallbackList(n => { + expect(log).toEqual([5, 4, 3, 2, 1]); + expect(n).toBe(15); + done(); + }); + + add(n => { + log.push(1); + next(n + 1); + }); + + add(n => { + log.push(2); + next(n + 1); + }); + + add(n => { + log.push(3); + next(n + 1); + }); + + add(n => { + log.push(4); + next(n + 1); + }); + + add(n => { + log.push(5); + next(n + 11); + }); + + next(0); + })); + + it('only calls complete once even if multiple next', () => + new Promise(done => { + const { add, next } = createCallbackList(n => { + expect(n).toBe(1); + done(); + }); - add(n => { - next(n + 1); - // We dont actually do this in our code... - next(n + 1); - }); + add(n => { + next(n + 1); + // We dont actually do this in our code... + next(n + 1); + }); - next(0); - }); + next(0); + })); - it('calls completed if added closure throws', done => { - const { add, next } = createCallbackList(n => { - expect(n).toBe(10); - done(); - }); + it('calls completed if added closure throws', () => + new Promise(done => { + const { add, next } = createCallbackList(n => { + expect(n).toBe(10); + done(); + }); - add(n => { - throw new Error('test'); - next(n + 1); - }); + add(n => { + throw new Error('test'); + next(n + 1); + }); - next(10); - }); + next(10); + })); }); describe('rateLimiter', () => { - it('calls disable if exceeded', done => { - const increment = createRateLimiter( - 5, - () => {}, - () => { - done(); - }, - ); - - for (let i = 0; i < 7; i++) { - increment(); - jest.advanceTimersByTime(100); - } - - jest.advanceTimersByTime(1_000); - }); + it('calls disable if exceeded', () => + new Promise(done => { + const increment = createRateLimiter( + 5, + () => {}, + () => { + done(); + }, + ); + + for (let i = 0; i < 7; i++) { + increment(); + vi.advanceTimersByTime(100); + } + + vi.advanceTimersByTime(1_000); + })); it('does not call disable if not exceeded', () => { const increment = createRateLimiter( @@ -106,38 +111,39 @@ describeIf(NODE_MAJOR >= 18)('LocalVariables', () => { for (let i = 0; i < 4; i++) { increment(); - jest.advanceTimersByTime(200); + vi.advanceTimersByTime(200); } - jest.advanceTimersByTime(600); + vi.advanceTimersByTime(600); for (let i = 0; i < 4; i++) { increment(); - jest.advanceTimersByTime(200); + vi.advanceTimersByTime(200); } }); - it('re-enables after timeout', done => { - let called = false; - - const increment = createRateLimiter( - 5, - () => { - expect(called).toEqual(true); - done(); - }, - () => { - expect(called).toEqual(false); - called = true; - }, - ); - - for (let i = 0; i < 10; i++) { - increment(); - jest.advanceTimersByTime(100); - } - - jest.advanceTimersByTime(10_000); - }); + it('re-enables after timeout', () => + new Promise(done => { + let called = false; + + const increment = createRateLimiter( + 5, + () => { + expect(called).toEqual(true); + done(); + }, + () => { + expect(called).toEqual(false); + called = true; + }, + ); + + for (let i = 0; i < 10; i++) { + increment(); + vi.advanceTimersByTime(100); + } + + vi.advanceTimersByTime(10_000); + })); }); }); diff --git a/packages/node/test/integrations/request-session-tracking.test.ts b/packages/node/test/integrations/request-session-tracking.test.ts index 7d5e4154d3fc..da4a6896a8ae 100644 --- a/packages/node/test/integrations/request-session-tracking.test.ts +++ b/packages/node/test/integrations/request-session-tracking.test.ts @@ -1,20 +1,21 @@ import { EventEmitter } from 'stream'; import { Scope, ServerRuntimeClient, createTransport, withScope } from '@sentry/core'; import type { Client } from '@sentry/core'; +import { describe, expect, it, vi } from 'vitest'; import { recordRequestSession } from '../../src/integrations/http/SentryHttpInstrumentation'; -jest.useFakeTimers(); +vi.useFakeTimers(); describe('recordRequestSession()', () => { it('should send an "exited" session for an ok ended request', () => { const client = createTestClient(); - const sendSessionSpy = jest.spyOn(client, 'sendSession'); + const sendSessionSpy = vi.spyOn(client, 'sendSession'); - jest.setSystemTime(new Date('March 19, 1999 06:12:34 UTC')); + vi.setSystemTime(new Date('March 19, 1999 06:12:34 UTC')); simulateRequest(client, 'ok'); - jest.runAllTimers(); + vi.runAllTimers(); expect(sendSessionSpy).toBeCalledWith({ aggregates: [{ crashed: 0, errored: 0, exited: 1, started: '1999-03-19T06:12:00.000Z' }], @@ -23,13 +24,13 @@ describe('recordRequestSession()', () => { it('should send an "crashed" session when the session on the requestProcessingMetadata was overridden with crashed', () => { const client = createTestClient(); - const sendSessionSpy = jest.spyOn(client, 'sendSession'); + const sendSessionSpy = vi.spyOn(client, 'sendSession'); - jest.setSystemTime(new Date('March 19, 1999 06:12:34 UTC')); + vi.setSystemTime(new Date('March 19, 1999 06:12:34 UTC')); simulateRequest(client, 'crashed'); - jest.runAllTimers(); + vi.runAllTimers(); expect(sendSessionSpy).toBeCalledWith({ aggregates: [{ crashed: 1, errored: 0, exited: 0, started: expect.stringMatching(/....-..-..T..:..:00.000Z/) }], @@ -38,13 +39,13 @@ describe('recordRequestSession()', () => { it('should send an "errored" session when the session on the requestProcessingMetadata was overridden with errored', () => { const client = createTestClient(); - const sendSessionSpy = jest.spyOn(client, 'sendSession'); + const sendSessionSpy = vi.spyOn(client, 'sendSession'); - jest.setSystemTime(new Date('March 19, 1999 06:12:34 UTC')); + vi.setSystemTime(new Date('March 19, 1999 06:12:34 UTC')); simulateRequest(client, 'errored'); - jest.runAllTimers(); + vi.runAllTimers(); expect(sendSessionSpy).toBeCalledWith({ aggregates: [{ crashed: 0, errored: 1, exited: 0, started: expect.stringMatching(/....-..-..T..:..:00.000Z/) }], @@ -54,9 +55,9 @@ describe('recordRequestSession()', () => { it('should aggregate request sessions within a time frame', async () => { const client = createTestClient(); - const sendSessionSpy = jest.spyOn(client, 'sendSession'); + const sendSessionSpy = vi.spyOn(client, 'sendSession'); - jest.setSystemTime(new Date('March 19, 1999 06:00:00 UTC')); + vi.setSystemTime(new Date('March 19, 1999 06:00:00 UTC')); simulateRequest(client, 'ok'); simulateRequest(client, 'ok'); @@ -64,12 +65,12 @@ describe('recordRequestSession()', () => { simulateRequest(client, 'errored'); // "Wait" 1+ second to get into new bucket - jest.setSystemTime(new Date('March 19, 1999 06:01:01 UTC')); + vi.setSystemTime(new Date('March 19, 1999 06:01:01 UTC')); simulateRequest(client, 'ok'); simulateRequest(client, 'errored'); - jest.runAllTimers(); + vi.runAllTimers(); expect(sendSessionSpy).toBeCalledWith({ aggregates: [ @@ -87,14 +88,14 @@ describe('recordRequestSession()', () => { it('should flush pending sessions when the client emits a "flush" hook', async () => { const client = createTestClient(); - const sendSessionSpy = jest.spyOn(client, 'sendSession'); + const sendSessionSpy = vi.spyOn(client, 'sendSession'); - jest.setSystemTime(new Date('March 19, 1999 06:00:00 UTC')); + vi.setSystemTime(new Date('March 19, 1999 06:00:00 UTC')); simulateRequest(client, 'ok'); // "Wait" 1+ second to get into new bucket - jest.setSystemTime(new Date('March 19, 1999 06:01:01 UTC')); + vi.setSystemTime(new Date('March 19, 1999 06:01:01 UTC')); simulateRequest(client, 'ok'); diff --git a/packages/node/test/integrations/spotlight.test.ts b/packages/node/test/integrations/spotlight.test.ts index fef4765c9b15..a26a36aced91 100644 --- a/packages/node/test/integrations/spotlight.test.ts +++ b/packages/node/test/integrations/spotlight.test.ts @@ -1,17 +1,27 @@ -import * as http from 'http'; +import * as http from 'node:http'; import { createEnvelope, logger } from '@sentry/core'; import type { Envelope, EventEnvelope } from '@sentry/core'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { spotlightIntegration } from '../../src/integrations/spotlight'; import { NodeClient } from '../../src/sdk/client'; import { getDefaultNodeClientOptions } from '../helpers/getDefaultNodeClientOptions'; +vi.mock('node:http', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const original = (await vi.importActual('node:http')) as typeof import('node:http'); + return { + ...original, + request: original.request, + }; +}); + describe('Spotlight', () => { - const loggerSpy = jest.spyOn(logger, 'warn'); + const loggerSpy = vi.spyOn(logger, 'warn'); afterEach(() => { loggerSpy.mockClear(); - jest.clearAllMocks(); + vi.clearAllMocks(); }); const options = getDefaultNodeClientOptions(); @@ -25,7 +35,7 @@ describe('Spotlight', () => { it('registers a callback on the `beforeEnvelope` hook', () => { const clientWithSpy = { ...client, - on: jest.fn(), + on: vi.fn(), }; const integration = spotlightIntegration(); // @ts-expect-error - this is fine in tests @@ -34,18 +44,18 @@ describe('Spotlight', () => { }); it('sends an envelope POST request to the sidecar url', () => { - const httpSpy = jest.spyOn(http, 'request').mockImplementationOnce(() => { + const httpSpy = vi.spyOn(http, 'request').mockImplementationOnce(() => { return { - on: jest.fn(), - write: jest.fn(), - end: jest.fn(), + on: vi.fn(), + write: vi.fn(), + end: vi.fn(), } as any; }); let callback: (envelope: Envelope) => void = () => {}; const clientWithSpy = { ...client, - on: jest.fn().mockImplementationOnce((_, cb) => (callback = cb)), + on: vi.fn().mockImplementationOnce((_, cb) => (callback = cb)), }; const integration = spotlightIntegration(); @@ -73,18 +83,18 @@ describe('Spotlight', () => { }); it('sends an envelope POST request to a custom sidecar url', () => { - const httpSpy = jest.spyOn(http, 'request').mockImplementationOnce(() => { + const httpSpy = vi.spyOn(http, 'request').mockImplementationOnce(() => { return { - on: jest.fn(), - write: jest.fn(), - end: jest.fn(), + on: vi.fn(), + write: vi.fn(), + end: vi.fn(), } as any; }); let callback: (envelope: Envelope) => void = () => {}; const clientWithSpy = { ...client, - on: jest.fn().mockImplementationOnce((_, cb) => (callback = cb)), + on: vi.fn().mockImplementationOnce((_, cb) => (callback = cb)), }; const integration = spotlightIntegration({ sidecarUrl: 'http://mylocalhost:8888/abcd' }); diff --git a/packages/node/test/integrations/tracing/graphql.test.ts b/packages/node/test/integrations/tracing/graphql.test.ts index 779b8dace830..10b8783c3702 100644 --- a/packages/node/test/integrations/tracing/graphql.test.ts +++ b/packages/node/test/integrations/tracing/graphql.test.ts @@ -1,15 +1,16 @@ import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql'; +import { type MockInstance, beforeEach, describe, expect, it, vi } from 'vitest'; import { graphqlIntegration, instrumentGraphql } from '../../../src/integrations/tracing/graphql'; import { INSTRUMENTED } from '../../../src/otel/instrument'; -jest.mock('@opentelemetry/instrumentation-graphql'); +vi.mock('@opentelemetry/instrumentation-graphql'); describe('GraphQL', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); delete INSTRUMENTED.Graphql; - (GraphQLInstrumentation as unknown as jest.SpyInstance).mockImplementation(() => { + (GraphQLInstrumentation as unknown as MockInstance).mockImplementation(() => { return { setTracerProvider: () => undefined, setMeterProvider: () => undefined, diff --git a/packages/node/test/integrations/tracing/mongo.test.ts b/packages/node/test/integrations/tracing/mongo.test.ts index 29571c07babe..c4944df08e16 100644 --- a/packages/node/test/integrations/tracing/mongo.test.ts +++ b/packages/node/test/integrations/tracing/mongo.test.ts @@ -1,5 +1,6 @@ import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb'; +import { type MockInstance, beforeEach, describe, expect, it, vi } from 'vitest'; import { _defaultDbStatementSerializer, instrumentMongo, @@ -7,14 +8,14 @@ import { } from '../../../src/integrations/tracing/mongo'; import { INSTRUMENTED } from '../../../src/otel/instrument'; -jest.mock('@opentelemetry/instrumentation-mongodb'); +vi.mock('@opentelemetry/instrumentation-mongodb'); describe('Mongo', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); delete INSTRUMENTED.Mongo; - (MongoDBInstrumentation as unknown as jest.SpyInstance).mockImplementation(() => { + (MongoDBInstrumentation as unknown as MockInstance).mockImplementation(() => { return { setTracerProvider: () => undefined, setMeterProvider: () => undefined, diff --git a/packages/node/test/integrations/tracing/redis.test.ts b/packages/node/test/integrations/tracing/redis.test.ts index 57eb727964be..a23d648b92d1 100644 --- a/packages/node/test/integrations/tracing/redis.test.ts +++ b/packages/node/test/integrations/tracing/redis.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { GET_COMMANDS, SET_COMMANDS, diff --git a/packages/node/test/sdk/api.test.ts b/packages/node/test/sdk/api.test.ts index 40510e46744e..e171d1e6ee02 100644 --- a/packages/node/test/sdk/api.test.ts +++ b/packages/node/test/sdk/api.test.ts @@ -1,9 +1,10 @@ import type { Event } from '@sentry/core'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { getActiveSpan, getClient, startInactiveSpan, startSpan, withActiveSpan } from '../../src'; import { cleanupOtel, mockSdkInit } from '../helpers/mockSdkInit'; afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); cleanupOtel(); }); @@ -21,7 +22,7 @@ describe('withActiveSpan()', () => { }); it('should create child spans when calling startSpan within the callback', async () => { - const beforeSendTransaction = jest.fn(() => null); + const beforeSendTransaction = vi.fn(() => null); mockSdkInit({ tracesSampleRate: 1, beforeSendTransaction }); const client = getClient(); @@ -66,7 +67,7 @@ describe('withActiveSpan()', () => { it('when `null` is passed, should start a new trace for new spans', async () => { const transactions: Event[] = []; - const beforeSendTransaction = jest.fn((event: Event) => { + const beforeSendTransaction = vi.fn((event: Event) => { transactions.push(event); return null; }); diff --git a/packages/node/test/sdk/client.test.ts b/packages/node/test/sdk/client.test.ts index 8eb66b78e82e..18b6c1d3ef53 100644 --- a/packages/node/test/sdk/client.test.ts +++ b/packages/node/test/sdk/client.test.ts @@ -4,6 +4,7 @@ import * as opentelemetryInstrumentationPackage from '@opentelemetry/instrumenta import type { Event, EventHint } from '@sentry/core'; import { SDK_VERSION, Scope, getCurrentScope, getGlobalScope, getIsolationScope } from '@sentry/core'; import { setOpenTelemetryContextAsyncContextStrategy } from '@sentry/opentelemetry'; +import { afterEach, beforeEach, describe, expect, it, test, vi } from 'vitest'; import { NodeClient } from '../../src'; import { getDefaultNodeClientOptions } from '../helpers/getDefaultNodeClientOptions'; import { cleanupOtel } from '../helpers/mockSdkInit'; @@ -18,7 +19,7 @@ describe('NodeClient', () => { }); afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); cleanupOtel(); }); @@ -162,7 +163,7 @@ describe('NodeClient', () => { }); const client = new NodeClient(options); - const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope'); + const sendEnvelopeSpy = vi.spyOn(client, 'sendEnvelope'); const id = client.captureCheckIn( { monitorSlug: 'foo', status: 'in_progress' }, @@ -232,7 +233,7 @@ describe('NodeClient', () => { }); const client = new NodeClient(options); - const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope'); + const sendEnvelopeSpy = vi.spyOn(client, 'sendEnvelope'); const id = client.captureCheckIn({ monitorSlug: 'heartbeat-monitor', status: 'ok' }); @@ -258,7 +259,7 @@ describe('NodeClient', () => { const options = getDefaultNodeClientOptions({ serverName: 'bar', enabled: false }); const client = new NodeClient(options); - const sendEnvelopeSpy = jest.spyOn(client, 'sendEnvelope'); + const sendEnvelopeSpy = vi.spyOn(client, 'sendEnvelope'); client.captureCheckIn({ monitorSlug: 'foo', status: 'in_progress' }); @@ -267,7 +268,7 @@ describe('NodeClient', () => { }); it('registers instrumentations provided with `openTelemetryInstrumentations`', () => { - const registerInstrumentationsSpy = jest + const registerInstrumentationsSpy = vi .spyOn(opentelemetryInstrumentationPackage, 'registerInstrumentations') .mockImplementationOnce(() => () => undefined); const instrumentationsArray = ['foobar'] as unknown as opentelemetryInstrumentationPackage.Instrumentation[]; diff --git a/packages/node/test/sdk/init.test.ts b/packages/node/test/sdk/init.test.ts index f6a02c2af4bd..a07e36a62d5a 100644 --- a/packages/node/test/sdk/init.test.ts +++ b/packages/node/test/sdk/init.test.ts @@ -2,6 +2,7 @@ import { logger } from '@sentry/core'; import type { Integration } from '@sentry/core'; import * as SentryOpentelemetry from '@sentry/opentelemetry'; +import { type Mock, type MockInstance, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { getClient } from '../../src/'; import * as auto from '../../src/integrations/tracing'; import { init, validateOpenTelemetrySetup } from '../../src/sdk'; @@ -15,25 +16,25 @@ const PUBLIC_DSN = 'https://username@domain/123'; class MockIntegration implements Integration { public name: string; - public setupOnce: jest.Mock = jest.fn(); + public setupOnce: Mock = vi.fn(); public constructor(name: string) { this.name = name; } } describe('init()', () => { - let mockAutoPerformanceIntegrations: jest.SpyInstance = jest.fn(() => []); + let mockAutoPerformanceIntegrations: MockInstance = vi.fn(() => []); beforeEach(() => { global.__SENTRY__ = {}; - mockAutoPerformanceIntegrations = jest.spyOn(auto, 'getAutoPerformanceIntegrations').mockImplementation(() => []); + mockAutoPerformanceIntegrations = vi.spyOn(auto, 'getAutoPerformanceIntegrations').mockImplementation(() => []); }); afterEach(() => { cleanupOtel(); - jest.clearAllMocks(); + vi.clearAllMocks(); }); describe('integrations', () => { @@ -64,10 +65,10 @@ describe('init()', () => { init({ dsn: PUBLIC_DSN, integrations: mockIntegrations, defaultIntegrations: mockDefaultIntegrations }); - expect(mockDefaultIntegrations[0]?.setupOnce as jest.Mock).toHaveBeenCalledTimes(0); - expect(mockDefaultIntegrations[1]?.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); - expect(mockIntegrations[0]?.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); - expect(mockIntegrations[1]?.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(mockDefaultIntegrations[0]?.setupOnce as Mock).toHaveBeenCalledTimes(0); + expect(mockDefaultIntegrations[1]?.setupOnce as Mock).toHaveBeenCalledTimes(1); + expect(mockIntegrations[0]?.setupOnce as Mock).toHaveBeenCalledTimes(1); + expect(mockIntegrations[1]?.setupOnce as Mock).toHaveBeenCalledTimes(1); expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(0); }); @@ -89,9 +90,9 @@ describe('init()', () => { }, }); - expect(mockDefaultIntegrations[0]?.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); - expect(mockDefaultIntegrations[1]?.setupOnce as jest.Mock).toHaveBeenCalledTimes(0); - expect(newIntegration.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(mockDefaultIntegrations[0]?.setupOnce as Mock).toHaveBeenCalledTimes(1); + expect(mockDefaultIntegrations[1]?.setupOnce as Mock).toHaveBeenCalledTimes(0); + expect(newIntegration.setupOnce as Mock).toHaveBeenCalledTimes(1); expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(0); }); @@ -111,9 +112,9 @@ describe('init()', () => { tracesSampleRate: 1, }); - expect(mockIntegrations[0]?.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); - expect(mockIntegrations[1]?.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); - expect(autoPerformanceIntegration.setupOnce as jest.Mock).toHaveBeenCalledTimes(1); + expect(mockIntegrations[0]?.setupOnce as Mock).toHaveBeenCalledTimes(1); + expect(mockIntegrations[1]?.setupOnce as Mock).toHaveBeenCalledTimes(1); + expect(autoPerformanceIntegration.setupOnce as Mock).toHaveBeenCalledTimes(1); expect(mockAutoPerformanceIntegrations).toHaveBeenCalledTimes(1); const client = getClient(); @@ -154,14 +155,14 @@ describe('validateOpenTelemetrySetup', () => { afterEach(() => { global.__SENTRY__ = {}; cleanupOtel(); - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('works with correct setup', () => { - const errorSpy = jest.spyOn(logger, 'error').mockImplementation(() => {}); - const warnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const errorSpy = vi.spyOn(logger, 'error').mockImplementation(() => {}); + const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); - jest.spyOn(SentryOpentelemetry, 'openTelemetrySetupCheck').mockImplementation(() => { + vi.spyOn(SentryOpentelemetry, 'openTelemetrySetupCheck').mockImplementation(() => { return ['SentryContextManager', 'SentryPropagator', 'SentrySampler']; }); @@ -172,10 +173,10 @@ describe('validateOpenTelemetrySetup', () => { }); it('works with missing setup, without tracing', () => { - const errorSpy = jest.spyOn(logger, 'error').mockImplementation(() => {}); - const warnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const errorSpy = vi.spyOn(logger, 'error').mockImplementation(() => {}); + const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); - jest.spyOn(SentryOpentelemetry, 'openTelemetrySetupCheck').mockImplementation(() => { + vi.spyOn(SentryOpentelemetry, 'openTelemetrySetupCheck').mockImplementation(() => { return []; }); @@ -191,10 +192,10 @@ describe('validateOpenTelemetrySetup', () => { }); it('works with missing setup, with tracing', () => { - const errorSpy = jest.spyOn(logger, 'error').mockImplementation(() => {}); - const warnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const errorSpy = vi.spyOn(logger, 'error').mockImplementation(() => {}); + const warnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); - jest.spyOn(SentryOpentelemetry, 'openTelemetrySetupCheck').mockImplementation(() => { + vi.spyOn(SentryOpentelemetry, 'openTelemetrySetupCheck').mockImplementation(() => { return []; }); diff --git a/packages/node/test/sdk/initOtel.test.ts b/packages/node/test/sdk/initOtel.test.ts index bb3c3a29c919..69c1da3fc258 100644 --- a/packages/node/test/sdk/initOtel.test.ts +++ b/packages/node/test/sdk/initOtel.test.ts @@ -1,27 +1,28 @@ import { logger } from '@sentry/core'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; import { _clampSpanProcessorTimeout } from '../../src/sdk/initOtel'; describe('_clampSpanProcessorTimeout', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('works with undefined', () => { - const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const loggerWarnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); const timeout = _clampSpanProcessorTimeout(undefined); expect(timeout).toBe(undefined); expect(loggerWarnSpy).not.toHaveBeenCalled(); }); it('works with positive number', () => { - const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const loggerWarnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); const timeout = _clampSpanProcessorTimeout(10); expect(timeout).toBe(10); expect(loggerWarnSpy).not.toHaveBeenCalled(); }); it('works with 0', () => { - const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const loggerWarnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); const timeout = _clampSpanProcessorTimeout(0); expect(timeout).toBe(undefined); expect(loggerWarnSpy).toHaveBeenCalledTimes(1); @@ -31,7 +32,7 @@ describe('_clampSpanProcessorTimeout', () => { }); it('works with negative number', () => { - const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const loggerWarnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); const timeout = _clampSpanProcessorTimeout(-10); expect(timeout).toBe(undefined); expect(loggerWarnSpy).toHaveBeenCalledTimes(1); @@ -41,7 +42,7 @@ describe('_clampSpanProcessorTimeout', () => { }); it('works with -Infinity', () => { - const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const loggerWarnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); const timeout = _clampSpanProcessorTimeout(-Infinity); expect(timeout).toBe(undefined); expect(loggerWarnSpy).toHaveBeenCalledTimes(1); @@ -51,7 +52,7 @@ describe('_clampSpanProcessorTimeout', () => { }); it('works with Infinity', () => { - const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const loggerWarnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); const timeout = _clampSpanProcessorTimeout(Infinity); expect(timeout).toBe(1_000_000); expect(loggerWarnSpy).toHaveBeenCalledTimes(1); @@ -59,7 +60,7 @@ describe('_clampSpanProcessorTimeout', () => { }); it('works with large number', () => { - const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const loggerWarnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); const timeout = _clampSpanProcessorTimeout(1_000_000_000); expect(timeout).toBe(1_000_000); expect(loggerWarnSpy).toHaveBeenCalledTimes(1); @@ -67,7 +68,7 @@ describe('_clampSpanProcessorTimeout', () => { }); it('works with NaN', () => { - const loggerWarnSpy = jest.spyOn(logger, 'warn').mockImplementation(() => {}); + const loggerWarnSpy = vi.spyOn(logger, 'warn').mockImplementation(() => {}); const timeout = _clampSpanProcessorTimeout(NaN); expect(timeout).toBe(undefined); expect(loggerWarnSpy).toHaveBeenCalledTimes(1); diff --git a/packages/node/test/sdk/preload.test.ts b/packages/node/test/sdk/preload.test.ts index 70dffe039a82..8daf8ada5c4b 100644 --- a/packages/node/test/sdk/preload.test.ts +++ b/packages/node/test/sdk/preload.test.ts @@ -1,18 +1,19 @@ import { logger } from '@sentry/core'; +import { afterEach, describe, expect, it, vi } from 'vitest'; describe('preload', () => { afterEach(() => { - jest.resetAllMocks(); + vi.resetAllMocks(); logger.disable(); delete process.env.SENTRY_DEBUG; delete process.env.SENTRY_PRELOAD_INTEGRATIONS; - jest.resetModules(); + vi.resetModules(); }); it('works without env vars', async () => { - const logSpy = jest.spyOn(console, 'log'); + const logSpy = vi.spyOn(console, 'log'); await import('../../src/preload'); @@ -20,9 +21,9 @@ describe('preload', () => { }); it('works with SENTRY_DEBUG set', async () => { - const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); // We want to swallow these logs - jest.spyOn(console, 'debug').mockImplementation(() => {}); + vi.spyOn(console, 'debug').mockImplementation(() => {}); process.env.SENTRY_DEBUG = '1'; @@ -34,9 +35,9 @@ describe('preload', () => { }); it('works with SENTRY_DEBUG & SENTRY_PRELOAD_INTEGRATIONS set', async () => { - const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); + const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); // We want to swallow these logs - jest.spyOn(console, 'debug').mockImplementation(() => {}); + vi.spyOn(console, 'debug').mockImplementation(() => {}); process.env.SENTRY_DEBUG = '1'; process.env.SENTRY_PRELOAD_INTEGRATIONS = 'Http,Express'; diff --git a/packages/node/test/transports/http.test.ts b/packages/node/test/transports/http.test.ts index 68b79f05f77d..4dcf31dd2661 100644 --- a/packages/node/test/transports/http.test.ts +++ b/packages/node/test/transports/http.test.ts @@ -4,13 +4,24 @@ import { createTransport } from '@sentry/core'; import { addItemToEnvelope, createAttachmentEnvelopeItem, createEnvelope, serializeEnvelope } from '@sentry/core'; import type { EventEnvelope, EventItem } from '@sentry/core'; +import { type Mock, afterEach, describe, expect, it, vi } from 'vitest'; import { makeNodeTransport } from '../../src/transports'; -jest.mock('@sentry/core', () => { - const actualCore = jest.requireActual('@sentry/core'); +vi.mock('@sentry/core', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const actualCore = (await vi.importActual('@sentry/core')) as typeof import('@sentry/core'); return { ...actualCore, - createTransport: jest.fn().mockImplementation(actualCore.createTransport), + createTransport: vi.fn().mockImplementation(actualCore.createTransport), + }; +}); + +vi.mock('node:http', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const original = (await vi.importActual('node:http')) as typeof import('node:http'); + return { + ...original, + request: original.request, }; }); @@ -78,17 +89,20 @@ const defaultOptions = { }; // empty function to keep test output clean -const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {}); +const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); -afterEach(done => { - jest.clearAllMocks(); +afterEach( + () => + new Promise(done => { + vi.clearAllMocks(); - if (testServer?.listening) { - testServer.close(done); - } else { - done(); - } -}); + if (testServer?.listening) { + testServer.close(() => done()); + } else { + done(); + } + }), +); describe('makeNewHttpTransport()', () => { describe('.send()', () => { @@ -206,7 +220,7 @@ describe('makeNewHttpTransport()', () => { }); describe('proxy', () => { - const proxyAgentSpy = jest + const proxyAgentSpy = vi .spyOn(httpProxyAgent, 'HttpsProxyAgent') // @ts-expect-error using http agent as https proxy agent .mockImplementation(() => new http.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 })); @@ -299,7 +313,7 @@ describe('makeNewHttpTransport()', () => { }); makeNodeTransport(defaultOptions); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + const registeredRequestExecutor = (createTransport as Mock).mock.calls[0]?.[1]; const executorResult = registeredRequestExecutor({ body: serializeEnvelope(EVENT_ENVELOPE), @@ -319,7 +333,7 @@ describe('makeNewHttpTransport()', () => { }); makeNodeTransport(defaultOptions); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + const registeredRequestExecutor = (createTransport as Mock).mock.calls[0]?.[1]; const executorResult = registeredRequestExecutor({ body: serializeEnvelope(EVENT_ENVELOPE), @@ -347,7 +361,7 @@ describe('makeNewHttpTransport()', () => { }); makeNodeTransport(defaultOptions); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + const registeredRequestExecutor = (createTransport as Mock).mock.calls[0]?.[1]; const executorResult = registeredRequestExecutor({ body: serializeEnvelope(EVENT_ENVELOPE), @@ -375,7 +389,7 @@ describe('makeNewHttpTransport()', () => { }); makeNodeTransport(defaultOptions); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + const registeredRequestExecutor = (createTransport as Mock).mock.calls[0]?.[1]; const executorResult = registeredRequestExecutor({ body: serializeEnvelope(EVENT_ENVELOPE), @@ -395,7 +409,7 @@ describe('makeNewHttpTransport()', () => { }); it('should create a noop transport if an invalid url is passed', async () => { - const requestSpy = jest.spyOn(http, 'request'); + const requestSpy = vi.spyOn(http, 'request'); const transport = makeNodeTransport({ ...defaultOptions, url: 'foo' }); await transport.send(EVENT_ENVELOPE); expect(requestSpy).not.toHaveBeenCalled(); diff --git a/packages/node/test/transports/https.test.ts b/packages/node/test/transports/https.test.ts index 3c1743178e87..b8a7d605b2f3 100644 --- a/packages/node/test/transports/https.test.ts +++ b/packages/node/test/transports/https.test.ts @@ -4,15 +4,17 @@ import { createTransport } from '@sentry/core'; import { createEnvelope, serializeEnvelope } from '@sentry/core'; import type { EventEnvelope, EventItem } from '@sentry/core'; +import { type Mock, afterEach, describe, expect, it, vi } from 'vitest'; import { makeNodeTransport } from '../../src/transports'; import type { HTTPModule, HTTPModuleRequestIncomingMessage } from '../../src/transports/http-module'; import testServerCerts from './test-server-certs'; -jest.mock('@sentry/core', () => { - const actualCore = jest.requireActual('@sentry/core'); +vi.mock('@sentry/core', async () => { + // eslint-disable-next-line @typescript-eslint/consistent-type-imports + const actualCore = (await vi.importActual('@sentry/core')) as typeof import('@sentry/core'); return { ...actualCore, - createTransport: jest.fn().mockImplementation(actualCore.createTransport), + createTransport: vi.fn().mockImplementation(actualCore.createTransport), }; }); @@ -69,7 +71,7 @@ const EVENT_ENVELOPE = createEnvelope({ event_id: 'aa3ff046696b4b const SERIALIZED_EVENT_ENVELOPE = serializeEnvelope(EVENT_ENVELOPE); const unsafeHttpsModule: HTTPModule = { - request: jest + request: vi .fn() .mockImplementation((options: https.RequestOptions, callback?: (res: HTTPModuleRequestIncomingMessage) => void) => { return https.request({ ...options, rejectUnauthorized: false }, callback); @@ -82,15 +84,18 @@ const defaultOptions = { recordDroppedEvent: () => undefined, // noop }; -afterEach(done => { - jest.clearAllMocks(); +afterEach( + () => + new Promise(done => { + vi.clearAllMocks(); - if (testServer?.listening) { - testServer.close(done); - } else { - done(); - } -}); + if (testServer?.listening) { + testServer.close(() => done()); + } else { + done(); + } + }), +); describe('makeNewHttpsTransport()', () => { describe('.send()', () => { @@ -180,7 +185,7 @@ describe('makeNewHttpsTransport()', () => { }); describe('proxy', () => { - const proxyAgentSpy = jest + const proxyAgentSpy = vi .spyOn(httpProxyAgent, 'HttpsProxyAgent') // @ts-expect-error using http agent as https proxy agent .mockImplementation(() => new http.Agent({ keepAlive: false, maxSockets: 30, timeout: 2000 })); @@ -291,7 +296,7 @@ describe('makeNewHttpsTransport()', () => { }); makeNodeTransport(defaultOptions); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + const registeredRequestExecutor = (createTransport as Mock).mock.calls[0]?.[1]; const executorResult = registeredRequestExecutor({ body: serializeEnvelope(EVENT_ENVELOPE), @@ -311,7 +316,7 @@ describe('makeNewHttpsTransport()', () => { }); makeNodeTransport(defaultOptions); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + const registeredRequestExecutor = (createTransport as Mock).mock.calls[0]?.[1]; const executorResult = registeredRequestExecutor({ body: serializeEnvelope(EVENT_ENVELOPE), @@ -339,7 +344,7 @@ describe('makeNewHttpsTransport()', () => { }); makeNodeTransport(defaultOptions); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + const registeredRequestExecutor = (createTransport as Mock).mock.calls[0]?.[1]; const executorResult = registeredRequestExecutor({ body: serializeEnvelope(EVENT_ENVELOPE), @@ -367,7 +372,7 @@ describe('makeNewHttpsTransport()', () => { }); makeNodeTransport(defaultOptions); - const registeredRequestExecutor = (createTransport as jest.Mock).mock.calls[0][1]; + const registeredRequestExecutor = (createTransport as Mock).mock.calls[0]?.[1]; const executorResult = registeredRequestExecutor({ body: serializeEnvelope(EVENT_ENVELOPE), diff --git a/packages/node/test/utils/ensureIsWrapped.test.ts b/packages/node/test/utils/ensureIsWrapped.test.ts index c328545315ce..890b913a4cb2 100644 --- a/packages/node/test/utils/ensureIsWrapped.test.ts +++ b/packages/node/test/utils/ensureIsWrapped.test.ts @@ -1,3 +1,4 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; import { ensureIsWrapped } from '../../src/utils/ensureIsWrapped'; import { cleanupOtel, mockSdkInit, resetGlobals } from '../helpers/mockSdkInit'; @@ -12,13 +13,13 @@ const wrappedfunction = Object.assign(() => {}, { describe('ensureIsWrapped', () => { afterEach(() => { - jest.restoreAllMocks(); + vi.restoreAllMocks(); cleanupOtel(); resetGlobals(); }); it('warns when the method is unwrapped', () => { - const spyWarn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const spyWarn = vi.spyOn(console, 'warn').mockImplementation(() => {}); mockSdkInit({ tracesSampleRate: 1 }); @@ -31,7 +32,7 @@ describe('ensureIsWrapped', () => { }); it('does not warn when the method is wrapped', () => { - const spyWarn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const spyWarn = vi.spyOn(console, 'warn').mockImplementation(() => {}); mockSdkInit({ tracesSampleRate: 1 }); @@ -41,7 +42,7 @@ describe('ensureIsWrapped', () => { }); it('does not warn without a client', () => { - const spyWarn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const spyWarn = vi.spyOn(console, 'warn').mockImplementation(() => {}); resetGlobals(); ensureIsWrapped(wrappedfunction, 'express'); @@ -50,7 +51,7 @@ describe('ensureIsWrapped', () => { }); it('does not warn without tracing', () => { - const spyWarn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const spyWarn = vi.spyOn(console, 'warn').mockImplementation(() => {}); mockSdkInit({}); @@ -60,7 +61,7 @@ describe('ensureIsWrapped', () => { }); it('does not warn if disableInstrumentationWarnings=true', () => { - const spyWarn = jest.spyOn(console, 'warn').mockImplementation(() => {}); + const spyWarn = vi.spyOn(console, 'warn').mockImplementation(() => {}); mockSdkInit({ tracesSampleRate: 1, disableInstrumentationWarnings: true }); diff --git a/packages/node/test/utils/entry-point.test.ts b/packages/node/test/utils/entry-point.test.ts index 5013482bca46..bd65ca5506c0 100644 --- a/packages/node/test/utils/entry-point.test.ts +++ b/packages/node/test/utils/entry-point.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import type { ProcessArgs, ProcessInterface } from '../../src/utils/entry-point'; import { getEntryPointType, parseProcessPaths } from '../../src/utils/entry-point'; diff --git a/packages/node/test/utils/envToBool.test.ts b/packages/node/test/utils/envToBool.test.ts index cd06ef132267..aa3c73fe1e8f 100644 --- a/packages/node/test/utils/envToBool.test.ts +++ b/packages/node/test/utils/envToBool.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { envToBool } from '../../src/utils/envToBool'; describe('envToBool', () => { diff --git a/packages/node/test/utils/getRequestUrl.test.ts b/packages/node/test/utils/getRequestUrl.test.ts index caa92aa10a59..621896987390 100644 --- a/packages/node/test/utils/getRequestUrl.test.ts +++ b/packages/node/test/utils/getRequestUrl.test.ts @@ -1,5 +1,6 @@ import type { RequestOptions } from 'http'; +import { describe, expect, it } from 'vitest'; import { getRequestUrl } from '../../src/utils/getRequestUrl'; describe('getRequestUrl', () => { diff --git a/packages/node/test/utils/module.test.ts b/packages/node/test/utils/module.test.ts index 1616bc7482d9..73404c37673e 100644 --- a/packages/node/test/utils/module.test.ts +++ b/packages/node/test/utils/module.test.ts @@ -1,3 +1,4 @@ +import { describe, expect, it } from 'vitest'; import { createGetModuleFromFilename } from '../../src'; describe('createGetModuleFromFilename', () => { diff --git a/packages/node/tsconfig.test.json b/packages/node/tsconfig.test.json index b0c6b000999b..3f2ffb86f0f7 100644 --- a/packages/node/tsconfig.test.json +++ b/packages/node/tsconfig.test.json @@ -1,11 +1,11 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*", "./src/integrations/diagnostic_channel.d.ts"], + "include": ["test/**/*", "./src/integrations/diagnostic_channel.d.ts", "vite.config.ts"], "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node"] // other package-specific, test-specific options } diff --git a/packages/node/vite.config.ts b/packages/node/vite.config.ts new file mode 100644 index 000000000000..f18ec92095bc --- /dev/null +++ b/packages/node/vite.config.ts @@ -0,0 +1,8 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, + test: { + ...baseConfig.test, + }, +}; diff --git a/packages/nuxt/src/runtime/plugins/sentry.client.ts b/packages/nuxt/src/runtime/plugins/sentry.client.ts index 39d62737f645..b15bff19f099 100644 --- a/packages/nuxt/src/runtime/plugins/sentry.client.ts +++ b/packages/nuxt/src/runtime/plugins/sentry.client.ts @@ -1,6 +1,6 @@ import { GLOBAL_OBJ, getClient } from '@sentry/core'; import { browserTracingIntegration, vueIntegration } from '@sentry/vue'; -import { defineNuxtPlugin } from 'nuxt/app'; +import { defineNuxtPlugin, isNuxtError } from 'nuxt/app'; import type { GlobalObjWithIntegrationOptions } from '../../client/vueIntegration'; import { reportNuxtError } from '../utils'; @@ -66,6 +66,12 @@ export default defineNuxtPlugin({ }); nuxtApp.hook('app:error', error => { + if (isNuxtError(error)) { + // Do not report if status code is 3xx or 4xx + if (error.statusCode >= 300 && error.statusCode < 500) { + return; + } + } reportNuxtError({ error }); }); diff --git a/packages/opentelemetry/package.json b/packages/opentelemetry/package.json index d33cd234851a..1d85c2fc4439 100644 --- a/packages/opentelemetry/package.json +++ b/packages/opentelemetry/package.json @@ -53,9 +53,9 @@ "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.30.1", "@opentelemetry/core": "^1.30.1", - "@opentelemetry/instrumentation": "^0.57.1", + "@opentelemetry/instrumentation": "^0.57.2", "@opentelemetry/sdk-trace-base": "^1.30.1", - "@opentelemetry/semantic-conventions": "^1.28.0" + "@opentelemetry/semantic-conventions": "^1.30.0" }, "scripts": { "build": "run-p build:transpile build:types", diff --git a/packages/opentelemetry/src/custom/client.ts b/packages/opentelemetry/src/custom/client.ts index ee9f21b5b5f5..70afb6f10752 100644 --- a/packages/opentelemetry/src/custom/client.ts +++ b/packages/opentelemetry/src/custom/client.ts @@ -18,12 +18,8 @@ import type { OpenTelemetryClient as OpenTelemetryClientInterface } from '../typ * const client = new OpenTelemetryClient(options); */ export function wrapClientClass< - ClassConstructor extends new ( - ...args: any[] - ) => Client, - WrappedClassConstructor extends new ( - ...args: any[] - ) => Client & OpenTelemetryClientInterface, + ClassConstructor extends new (...args: any[]) => Client, + WrappedClassConstructor extends new (...args: any[]) => Client & OpenTelemetryClientInterface, >(ClientClass: ClassConstructor): WrappedClassConstructor { // @ts-expect-error We just assume that this is non-abstract, if you pass in an abstract class this would make it non-abstract class OpenTelemetryClient extends ClientClass implements OpenTelemetryClientInterface { diff --git a/packages/opentelemetry/src/index.ts b/packages/opentelemetry/src/index.ts index e3ae6536e11e..6958d1c9fbdd 100644 --- a/packages/opentelemetry/src/index.ts +++ b/packages/opentelemetry/src/index.ts @@ -41,15 +41,9 @@ export { setupEventContextTrace } from './setupEventContextTrace'; export { setOpenTelemetryContextAsyncContextStrategy } from './asyncContextStrategy'; export { wrapContextManagerClass } from './contextManager'; -export { - SentryPropagator, - shouldPropagateTraceForUrl, -} from './propagator'; +export { SentryPropagator, shouldPropagateTraceForUrl } from './propagator'; export { SentrySpanProcessor } from './spanProcessor'; -export { - SentrySampler, - wrapSamplingDecision, -} from './sampler'; +export { SentrySampler, wrapSamplingDecision } from './sampler'; export { openTelemetrySetupCheck } from './utils/setupCheck'; diff --git a/packages/profiling-node/.eslintrc.js b/packages/profiling-node/.eslintrc.js index e4cb54c2f08c..22c20a12dd77 100644 --- a/packages/profiling-node/.eslintrc.js +++ b/packages/profiling-node/.eslintrc.js @@ -4,7 +4,7 @@ module.exports = { }, extends: ['../../.eslintrc.js'], - ignorePatterns: ['lib/**/*', 'examples/**/*', 'jest.co'], + ignorePatterns: ['lib/**/*', 'examples/**/*', 'vitest.config.ts'], rules: { '@sentry-internal/sdk/no-class-field-initializers': 'off', }, diff --git a/packages/profiling-node/jest.config.js b/packages/profiling-node/jest.config.js deleted file mode 100644 index 89bda645921b..000000000000 --- a/packages/profiling-node/jest.config.js +++ /dev/null @@ -1,6 +0,0 @@ -const baseConfig = require('../../jest/jest.config.js'); - -module.exports = { - ...baseConfig, - testEnvironment: 'node', -}; diff --git a/packages/profiling-node/package.json b/packages/profiling-node/package.json index 57dc28ba9c7b..158e4f32d91e 100644 --- a/packages/profiling-node/package.json +++ b/packages/profiling-node/package.json @@ -57,9 +57,9 @@ "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch", "build:watch": "run-p build:transpile:watch build:types:watch", "build:tarball": "npm pack", - "test:watch": "jest --watch", "test:bundle": "node test-binaries.esbuild.js", - "test": "jest --config jest.config.js" + "test": "vitest run", + "test:watch": "vitest --watch" }, "dependencies": { "@sentry-internal/node-cpu-profiler": "^2.0.0", diff --git a/packages/profiling-node/test/integration.test.ts b/packages/profiling-node/test/integration.test.ts index 39788b00bc56..392e02ed7704 100644 --- a/packages/profiling-node/test/integration.test.ts +++ b/packages/profiling-node/test/integration.test.ts @@ -3,11 +3,12 @@ import { EventEmitter } from 'events'; import type { Transport } from '@sentry/core'; import type { NodeClient } from '@sentry/node'; +import { afterEach, describe, expect, it, vi } from 'vitest'; import { _nodeProfilingIntegration } from '../src/integration'; describe('ProfilingIntegration', () => { afterEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it('has a name', () => { expect(_nodeProfilingIntegration().name).toBe('ProfilingIntegration'); @@ -15,8 +16,8 @@ describe('ProfilingIntegration', () => { it('does not call transporter if null profile is received', () => { const transport: Transport = { - send: jest.fn().mockImplementation(() => Promise.resolve()), - flush: jest.fn().mockImplementation(() => Promise.resolve()), + send: vi.fn().mockImplementation(() => Promise.resolve()), + flush: vi.fn().mockImplementation(() => Promise.resolve()), }; const integration = _nodeProfilingIntegration(); const emitter = new EventEmitter(); @@ -43,14 +44,14 @@ describe('ProfilingIntegration', () => { it('binds to spanStart, spanEnd and beforeEnvelope', () => { const transport: Transport = { - send: jest.fn().mockImplementation(() => Promise.resolve()), - flush: jest.fn().mockImplementation(() => Promise.resolve()), + send: vi.fn().mockImplementation(() => Promise.resolve()), + flush: vi.fn().mockImplementation(() => Promise.resolve()), }; const integration = _nodeProfilingIntegration(); const client = { - on: jest.fn(), - emit: jest.fn(), + on: vi.fn(), + emit: vi.fn(), getOptions: () => { return { _metadata: {}, @@ -63,7 +64,7 @@ describe('ProfilingIntegration', () => { getTransport: () => transport, } as unknown as NodeClient; - const spy = jest.spyOn(client, 'on'); + const spy = vi.spyOn(client, 'on'); integration?.setup?.(client); diff --git a/packages/profiling-node/test/spanProfileUtils.test.ts b/packages/profiling-node/test/spanProfileUtils.test.ts index c0640064c537..5aae42783c86 100644 --- a/packages/profiling-node/test/spanProfileUtils.test.ts +++ b/packages/profiling-node/test/spanProfileUtils.test.ts @@ -1,4 +1,5 @@ import * as Sentry from '@sentry/node'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { CpuProfilerBindings } from '@sentry-internal/node-cpu-profiler'; import { getMainCarrier } from '@sentry/core'; @@ -80,14 +81,14 @@ const wait = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)); describe('automated span instrumentation', () => { beforeEach(() => { - jest.useRealTimers(); + vi.useRealTimers(); // We will mock the carrier as if it has been initialized by the SDK, else everything is short circuited getMainCarrier().__SENTRY__ = {}; GLOBAL_OBJ._sentryDebugIds = undefined as any; }); afterEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); + vi.clearAllMocks(); + vi.restoreAllMocks(); delete getMainCarrier().__SENTRY__; }); @@ -96,7 +97,7 @@ describe('automated span instrumentation', () => { Sentry.setCurrentClient(client); client.init(); - const transportSpy = jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + const transportSpy = vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); const transaction = Sentry.startInactiveSpan({ forceTransaction: true, name: 'profile_hub' }); await wait(500); @@ -107,14 +108,14 @@ describe('automated span instrumentation', () => { }); it('logger warns user if there are insufficient samples and discards the profile', async () => { - const logSpy = jest.spyOn(logger, 'log'); + const logSpy = vi.spyOn(logger, 'log'); const [client, transport] = makeClientWithHooks(); Sentry.setCurrentClient(client); client.init(); // @ts-expect-error we just mock the return type and ignore the signature - jest.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => { + vi.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => { return { samples: [ { @@ -131,7 +132,7 @@ describe('automated span instrumentation', () => { }; }); - jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); const transaction = Sentry.startInactiveSpan({ forceTransaction: true, name: 'profile_hub' }); transaction.end(); @@ -146,14 +147,14 @@ describe('automated span instrumentation', () => { }); it('logger warns user if traceId is invalid', async () => { - const logSpy = jest.spyOn(logger, 'log'); + const logSpy = vi.spyOn(logger, 'log'); const [client, transport] = makeClientWithHooks(); Sentry.setCurrentClient(client); client.init(); // @ts-expect-error we just mock the return type and ignore the signature - jest.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => { + vi.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => { return { samples: [ { @@ -175,7 +176,7 @@ describe('automated span instrumentation', () => { }; }); - jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); Sentry.getCurrentScope().getPropagationContext().traceId = 'boop'; const transaction = Sentry.startInactiveSpan({ @@ -196,10 +197,10 @@ describe('automated span instrumentation', () => { Sentry.setCurrentClient(client); client.init(); - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); - const stopProfilingSpy = jest.spyOn(CpuProfilerBindings, 'stopProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); + const stopProfilingSpy = vi.spyOn(CpuProfilerBindings, 'stopProfiling'); - jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); const transaction = Sentry.startInactiveSpan({ forceTransaction: true, name: 'profile_hub' }); await wait(500); @@ -216,7 +217,7 @@ describe('automated span instrumentation', () => { Sentry.setCurrentClient(client); client.init(); - const transportSpy = jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + const transportSpy = vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); const transaction = Sentry.startInactiveSpan({ forceTransaction: true, name: 'profile_hub' }); await wait(500); @@ -258,9 +259,9 @@ describe('automated span instrumentation', () => { Sentry.setCurrentClient(client); client.init(); - jest.spyOn(CpuProfilerBindings, 'stopProfiling').mockReturnValue(null); + vi.spyOn(CpuProfilerBindings, 'stopProfiling').mockReturnValue(null); // Emit is sync, so we can just assert that we got here - const transportSpy = jest.spyOn(transport, 'send').mockImplementation(() => { + const transportSpy = vi.spyOn(transport, 'send').mockImplementation(() => { // Do nothing so we don't send events to Sentry return Promise.resolve({}); }); @@ -281,7 +282,7 @@ describe('automated span instrumentation', () => { Sentry.setCurrentClient(client); client.init(); - const onPreprocessEvent = jest.fn(); + const onPreprocessEvent = vi.fn(); client.on('preprocessEvent', onPreprocessEvent); @@ -291,7 +292,7 @@ describe('automated span instrumentation', () => { await Sentry.flush(1000); - expect(onPreprocessEvent.mock.calls[1][0]).toMatchObject({ + expect(onPreprocessEvent.mock.calls[1]?.[0]).toMatchObject({ profile: { samples: expect.arrayContaining([expect.anything()]), stacks: expect.arrayContaining([expect.anything()]), @@ -300,7 +301,7 @@ describe('automated span instrumentation', () => { }); it('automated span instrumentation does not support continuous profiling', () => { - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); const [client] = makeClientWithHooks(); Sentry.setCurrentClient(client); @@ -316,7 +317,7 @@ describe('automated span instrumentation', () => { }); it('does not crash if stop is called multiple times', async () => { - const stopProfilingSpy = jest.spyOn(CpuProfilerBindings, 'stopProfiling'); + const stopProfilingSpy = vi.spyOn(CpuProfilerBindings, 'stopProfiling'); const [client] = makeClientWithHooks(); Sentry.setCurrentClient(client); @@ -335,7 +336,7 @@ describe('automated span instrumentation', () => { }; // @ts-expect-error we just mock the return type and ignore the signature - jest.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => { + vi.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => { return { samples: [ { @@ -361,7 +362,7 @@ describe('automated span instrumentation', () => { Sentry.setCurrentClient(client); client.init(); - const transportSpy = jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + const transportSpy = vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); const transaction = Sentry.startInactiveSpan({ forceTransaction: true, name: 'profile_hub' }); await wait(500); @@ -390,7 +391,7 @@ describe('automated span instrumentation', () => { describe('continuous profiling', () => { beforeEach(() => { - jest.useFakeTimers(); + vi.useFakeTimers(); // We will mock the carrier as if it has been initialized by the SDK, else everything is short circuited getMainCarrier().__SENTRY__ = {}; GLOBAL_OBJ._sentryDebugIds = undefined as any; @@ -403,15 +404,15 @@ describe('continuous profiling', () => { Sentry.profiler.stopProfiler(); } - jest.clearAllMocks(); - jest.restoreAllMocks(); - jest.runAllTimers(); + vi.clearAllMocks(); + vi.restoreAllMocks(); + vi.runAllTimers(); delete getMainCarrier().__SENTRY__; }); it('attaches sdk metadata to chunks', () => { // @ts-expect-error we just mock the return type and ignore the signature - jest.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => { + vi.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => { return { samples: [ { @@ -437,11 +438,11 @@ describe('continuous profiling', () => { Sentry.setCurrentClient(client); client.init(); - const transportSpy = jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + const transportSpy = vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); Sentry.profiler.startProfiler(); - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); Sentry.profiler.stopProfiler(); - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); const profile = transportSpy.mock.calls?.[0]?.[0]?.[1]?.[0]?.[1] as ProfileChunk; expect(profile.client_sdk.name).toBe('sentry.javascript.node'); @@ -449,7 +450,7 @@ describe('continuous profiling', () => { }); it('initializes the continuous profiler', () => { - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); const [client] = makeContinuousProfilingClient(); Sentry.setCurrentClient(client); @@ -463,7 +464,7 @@ describe('continuous profiling', () => { }); it('starts a continuous profile', () => { - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); const [client] = makeContinuousProfilingClient(); Sentry.setCurrentClient(client); @@ -475,8 +476,8 @@ describe('continuous profiling', () => { }); it('multiple calls to start abort previous profile', () => { - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); - const stopProfilingSpy = jest.spyOn(CpuProfilerBindings, 'stopProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); + const stopProfilingSpy = vi.spyOn(CpuProfilerBindings, 'stopProfiling'); const [client] = makeContinuousProfilingClient(); Sentry.setCurrentClient(client); @@ -491,8 +492,8 @@ describe('continuous profiling', () => { }); it('restarts a new chunk after previous', async () => { - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); - const stopProfilingSpy = jest.spyOn(CpuProfilerBindings, 'stopProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); + const stopProfilingSpy = vi.spyOn(CpuProfilerBindings, 'stopProfiling'); const [client] = makeContinuousProfilingClient(); Sentry.setCurrentClient(client); @@ -501,14 +502,14 @@ describe('continuous profiling', () => { expect(startProfilingSpy).not.toHaveBeenCalledTimes(1); Sentry.profiler.startProfiler(); - jest.advanceTimersByTime(60_001); + vi.advanceTimersByTime(60_001); expect(stopProfilingSpy).toHaveBeenCalledTimes(1); expect(startProfilingSpy).toHaveBeenCalledTimes(2); }); it('chunks share the same profilerId', async () => { - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); - const stopProfilingSpy = jest.spyOn(CpuProfilerBindings, 'stopProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); + const stopProfilingSpy = vi.spyOn(CpuProfilerBindings, 'stopProfiling'); const [client] = makeContinuousProfilingClient(); Sentry.setCurrentClient(client); @@ -518,14 +519,14 @@ describe('continuous profiling', () => { Sentry.profiler.startProfiler(); const profilerId = getProfilerId(); - jest.advanceTimersByTime(60_001); + vi.advanceTimersByTime(60_001); expect(stopProfilingSpy).toHaveBeenCalledTimes(1); expect(startProfilingSpy).toHaveBeenCalledTimes(2); expect(getProfilerId()).toBe(profilerId); }); it('explicit calls to stop clear profilerId', async () => { - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); const [client] = makeContinuousProfilingClient(); Sentry.setCurrentClient(client); @@ -542,8 +543,8 @@ describe('continuous profiling', () => { }); it('stops a continuous profile after interval', async () => { - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); - const stopProfilingSpy = jest.spyOn(CpuProfilerBindings, 'stopProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); + const stopProfilingSpy = vi.spyOn(CpuProfilerBindings, 'stopProfiling'); const [client] = makeContinuousProfilingClient(); Sentry.setCurrentClient(client); @@ -552,13 +553,13 @@ describe('continuous profiling', () => { expect(startProfilingSpy).not.toHaveBeenCalledTimes(1); Sentry.profiler.startProfiler(); - jest.advanceTimersByTime(60_001); + vi.advanceTimersByTime(60_001); expect(stopProfilingSpy).toHaveBeenCalledTimes(1); }); it('manually stopping a chunk doesnt restart the profiler', async () => { - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); - const stopProfilingSpy = jest.spyOn(CpuProfilerBindings, 'stopProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); + const stopProfilingSpy = vi.spyOn(CpuProfilerBindings, 'stopProfiling'); const [client] = makeContinuousProfilingClient(); Sentry.setCurrentClient(client); @@ -567,17 +568,17 @@ describe('continuous profiling', () => { expect(startProfilingSpy).not.toHaveBeenCalledTimes(1); Sentry.profiler.startProfiler(); - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); Sentry.profiler.stopProfiler(); expect(stopProfilingSpy).toHaveBeenCalledTimes(1); - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); expect(startProfilingSpy).toHaveBeenCalledTimes(1); }); it('continuous mode does not instrument spans', () => { - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); const [client] = makeContinuousProfilingClient(); Sentry.setCurrentClient(client); @@ -589,7 +590,7 @@ describe('continuous profiling', () => { it('sends as profile_chunk envelope type', async () => { // @ts-expect-error we just mock the return type and ignore the signature - jest.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => { + vi.spyOn(CpuProfilerBindings, 'stopProfiling').mockImplementation(() => { return { samples: [ { @@ -615,11 +616,11 @@ describe('continuous profiling', () => { Sentry.setCurrentClient(client); client.init(); - const transportSpy = jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + const transportSpy = vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); Sentry.profiler.startProfiler(); - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); Sentry.profiler.stopProfiler(); - jest.advanceTimersByTime(1000); + vi.advanceTimersByTime(1000); expect(transportSpy.mock.calls?.[0]?.[0]?.[1]?.[0]?.[0]?.type).toBe('profile_chunk'); }); @@ -629,7 +630,7 @@ describe('continuous profiling', () => { Sentry.setCurrentClient(client); client.init(); - const transportSpy = jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + const transportSpy = vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); const nonProfiledTransaction = Sentry.startInactiveSpan({ forceTransaction: true, name: 'profile_hub' }); nonProfiledTransaction.end(); @@ -671,7 +672,7 @@ describe('continuous profiling does not start in span profiling mode', () => { ['profilesSampleRate=1', makeClientOptions({ profilesSampleRate: 1 })], ['profilesSampler is defined', makeClientOptions({ profilesSampler: () => 1 })], ])('%s', async (_label, options) => { - const logSpy = jest.spyOn(logger, 'log'); + const logSpy = vi.spyOn(logger, 'log'); const client = new Sentry.NodeClient({ ...options, dsn: 'https://7fa19397baaf433f919fbe02228d5470@o1137848.ingest.sentry.io/6625302', @@ -689,14 +690,14 @@ describe('continuous profiling does not start in span profiling mode', () => { Sentry.setCurrentClient(client); client.init(); - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); const transport = client.getTransport(); if (!transport) { throw new Error('Transport not found'); } - jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); Sentry.startInactiveSpan({ forceTransaction: true, name: 'profile_hub' }); expect(startProfilingSpy).toHaveBeenCalled(); @@ -714,7 +715,7 @@ describe('continuous profiling does not start in span profiling mode', () => { }); describe('continuous profiling mode', () => { beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); }); it.each([ @@ -744,14 +745,14 @@ describe('continuous profiling mode', () => { Sentry.setCurrentClient(client); client.init(); - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); const transport = client.getTransport(); if (!transport) { throw new Error('Transport not found'); } - jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); Sentry.profiler.startProfiler(); const callCount = startProfilingSpy.mock.calls.length; expect(startProfilingSpy).toHaveBeenCalled(); @@ -778,8 +779,8 @@ describe('continuous profiling mode', () => { Sentry.setCurrentClient(client); client.init(); - const startProfilingSpy = jest.spyOn(CpuProfilerBindings, 'startProfiling'); - const stopProfilingSpy = jest.spyOn(CpuProfilerBindings, 'stopProfiling'); + const startProfilingSpy = vi.spyOn(CpuProfilerBindings, 'startProfiling'); + const stopProfilingSpy = vi.spyOn(CpuProfilerBindings, 'stopProfiling'); Sentry.profiler.startProfiler(); expect(startProfilingSpy).toHaveBeenCalledTimes(1); diff --git a/packages/profiling-node/test/spanProfileUtils.worker.test.ts b/packages/profiling-node/test/spanProfileUtils.worker.test.ts index e2db2ecb7706..95b1a7bfad3f 100644 --- a/packages/profiling-node/test/spanProfileUtils.worker.test.ts +++ b/packages/profiling-node/test/spanProfileUtils.worker.test.ts @@ -1,16 +1,17 @@ +import type { Transport } from '@sentry/core'; +import { type ProfilingIntegration } from '@sentry/core'; +import * as Sentry from '@sentry/node'; +import { expect, it, vi } from 'vitest'; +import { _nodeProfilingIntegration } from '../src/integration'; + // Mock the modules before the import, so that the value is initialized before the module is loaded -jest.mock('worker_threads', () => { +vi.mock('worker_threads', () => { return { isMainThread: false, threadId: 9999, }; }); -jest.setTimeout(10000); - -import type { Transport } from '@sentry/core'; -import { type ProfilingIntegration } from '@sentry/core'; -import * as Sentry from '@sentry/node'; -import { _nodeProfilingIntegration } from '../src/integration'; +vi.setConfig({ testTimeout: 10_000 }); function makeContinuousProfilingClient(): [Sentry.NodeClient, Transport] { const integration = _nodeProfilingIntegration(); @@ -39,7 +40,7 @@ it('worker threads context', () => { Sentry.setCurrentClient(client); client.init(); - const transportSpy = jest.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); + const transportSpy = vi.spyOn(transport, 'send').mockReturnValue(Promise.resolve({})); const nonProfiledTransaction = Sentry.startInactiveSpan({ forceTransaction: true, name: 'profile_hub' }); nonProfiledTransaction.end(); diff --git a/packages/profiling-node/test/utils.test.ts b/packages/profiling-node/test/utils.test.ts index 2e5b79e9baee..2fd925981e28 100644 --- a/packages/profiling-node/test/utils.test.ts +++ b/packages/profiling-node/test/utils.test.ts @@ -1,5 +1,6 @@ import { addItemToEnvelope, createEnvelope, uuid4 } from '@sentry/core'; import type { Event } from '@sentry/core'; +import { describe, expect, it } from 'vitest'; import type { RawThreadCpuProfile } from '@sentry-internal/node-cpu-profiler'; import { diff --git a/packages/profiling-node/tsconfig.test.json b/packages/profiling-node/tsconfig.test.json index 52333183eb70..c401c76a5305 100644 --- a/packages/profiling-node/tsconfig.test.json +++ b/packages/profiling-node/tsconfig.test.json @@ -1,11 +1,11 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*", "src/**/*.d.ts"], + "include": ["test/**/*", "src/**/*.d.ts", "vite.config.ts"], "compilerOptions": { // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["node", "jest"] + "types": ["node"] // other package-specific, test-specific options } diff --git a/packages/profiling-node/vite.config.ts b/packages/profiling-node/vite.config.ts new file mode 100644 index 000000000000..f18ec92095bc --- /dev/null +++ b/packages/profiling-node/vite.config.ts @@ -0,0 +1,8 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, + test: { + ...baseConfig.test, + }, +}; diff --git a/packages/react-router/README.md b/packages/react-router/README.md index 368f3ee4b717..0e0890260c71 100644 --- a/packages/react-router/README.md +++ b/packages/react-router/README.md @@ -4,7 +4,7 @@

-# Official Sentry SDK for React Router (Framework) (EXPERIMENTAL) +# Official Sentry SDK for React Router Framework (EXPERIMENTAL) [![npm version](https://img.shields.io/npm/v/@sentry/react-router.svg)](https://www.npmjs.com/package/@sentry/react-router) [![npm dm](https://img.shields.io/npm/dm/@sentry/react-router.svg)](https://www.npmjs.com/package/@sentry/react-router) @@ -107,7 +107,7 @@ export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { Create an `instrument.server.mjs` file in the root of your app: ```js -import * as Sentry from '@sentry/node'; +import * as Sentry from '@sentry/react-router'; Sentry.init({ dsn: '___PUBLIC_DSN___', @@ -118,7 +118,7 @@ Sentry.init({ In your `entry.server.tsx` file, export the `handleError` function: ```tsx -import * as Sentry from '@sentry/node'; +import * as Sentry from '@sentry/react-router'; import { type HandleErrorFunction } from 'react-router'; export const handleError: HandleErrorFunction = (error, { request }) => { @@ -144,3 +144,39 @@ Update the `start` and `dev` script to include the instrumentation file: "start": "NODE_OPTIONS='--import ./instrument.server.mjs' react-router-serve ./build/server/index.js", } ``` + +## Build-time Config + +Update your vite.config.ts file to include the `sentryReactRouter` plugin and also add your config options to the vite config (this is required for uploading sourcemaps at the end of the build): + +```ts +import { reactRouter } from '@react-router/dev/vite'; +import { sentryReactRouter } from '@sentry/react-router'; +import { defineConfig } from 'vite'; + +const sentryConfig = { + authToken: '...', + org: '...', + project: '...', + // rest of your config +}; + +export default defineConfig(config => { + return { + plugins: [reactRouter(), sentryReactRouter(sentryConfig, config)], + sentryConfig, + }; +}); +``` + +Next, in your `react-router.config.ts` file, include the `sentryOnBuildEnd` hook: + +```ts +import type { Config } from '@react-router/dev/config'; +import { sentryOnBuildEnd } from '@sentry/react-router'; + +export default { + ssr: true, + buildEnd: sentryOnBuildEnd, +} satisfies Config; +``` diff --git a/packages/react-router/package.json b/packages/react-router/package.json index 25b9f8e807ef..4668ff85addb 100644 --- a/packages/react-router/package.json +++ b/packages/react-router/package.json @@ -34,13 +34,18 @@ "access": "public" }, "dependencies": { + "@sentry/vite-plugin": "^3.2.0", + "@sentry/cli": "^2.42.1", + "glob": "11.0.1", "@sentry/browser": "9.2.0", "@sentry/core": "9.2.0", "@sentry/node": "9.2.0" }, "devDependencies": { "@react-router/node": "^7.1.5", - "react-router": "^7.1.5" + "@react-router/dev": "^7.1.5", + "react-router": "^7.1.5", + "vite": "^6.1.0" }, "peerDependencies": { "@react-router/node": "7.x", diff --git a/packages/react-router/rollup.npm.config.mjs b/packages/react-router/rollup.npm.config.mjs index 65e6b0a0dd9a..709de91a7b6a 100644 --- a/packages/react-router/rollup.npm.config.mjs +++ b/packages/react-router/rollup.npm.config.mjs @@ -5,7 +5,7 @@ export default [ makeBaseNPMConfig({ entrypoints: ['src/index.server.ts', 'src/index.client.ts'], packageSpecificConfig: { - external: ['react-router', 'react-router-dom', 'react', 'react/jsx-runtime'], + external: ['react-router', 'react-router-dom', 'react', 'react/jsx-runtime', 'vite'], output: { // make it so Rollup calms down about the fact that we're combining default and named exports exports: 'named', diff --git a/packages/react-router/src/index.server.ts b/packages/react-router/src/index.server.ts index 0ce5251aa327..d675a1c72820 100644 --- a/packages/react-router/src/index.server.ts +++ b/packages/react-router/src/index.server.ts @@ -1 +1,2 @@ export * from './server'; +export * from './vite'; diff --git a/packages/react-router/src/index.types.ts b/packages/react-router/src/index.types.ts index 8d246451eb7d..4d7fb425f5ee 100644 --- a/packages/react-router/src/index.types.ts +++ b/packages/react-router/src/index.types.ts @@ -2,6 +2,7 @@ export * from './client'; export * from './server'; +export * from './vite'; import type { Integration, Options, StackParser } from '@sentry/core'; import type * as clientSdk from './client'; diff --git a/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts b/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts new file mode 100644 index 000000000000..ee89cf1a09e3 --- /dev/null +++ b/packages/react-router/src/vite/buildEnd/handleOnBuildEnd.ts @@ -0,0 +1,113 @@ +import { rm } from 'node:fs/promises'; +import type { Config } from '@react-router/dev/dist/config'; +import SentryCli from '@sentry/cli'; +import { glob } from 'glob'; +import type { SentryReactRouterBuildOptions } from '../types'; + +type BuildEndHook = NonNullable; + +function getSentryConfig(viteConfig: unknown): SentryReactRouterBuildOptions { + if (!viteConfig || typeof viteConfig !== 'object' || !('sentryConfig' in viteConfig)) { + // eslint-disable-next-line no-console + console.error('[Sentry] sentryConfig not found - it needs to be passed to vite.config.ts'); + } + + return (viteConfig as { sentryConfig: SentryReactRouterBuildOptions }).sentryConfig; +} + +/** + * A build end hook that handles Sentry release creation and source map uploads. + * It creates a new Sentry release if configured, uploads source maps to Sentry, + * and optionally deletes the source map files after upload. + */ +export const sentryOnBuildEnd: BuildEndHook = async ({ reactRouterConfig, viteConfig }) => { + const { + authToken, + org, + project, + release, + sourceMapsUploadOptions = { enabled: true }, + debug = false, + } = getSentryConfig(viteConfig); + + const cliInstance = new SentryCli(null, { + authToken, + org, + project, + }); + // check if release should be created + if (release?.name) { + try { + await cliInstance.releases.new(release.name); + } catch (error) { + // eslint-disable-next-line no-console + console.error('[Sentry] Could not create release', error); + } + } + + if (sourceMapsUploadOptions?.enabled ?? (true && viteConfig.build.sourcemap !== false)) { + // inject debugIds + try { + await cliInstance.execute(['sourcemaps', 'inject', reactRouterConfig.buildDirectory], debug); + } catch (error) { + // eslint-disable-next-line no-console + console.error('[Sentry] Could not inject debug ids', error); + } + + // upload sourcemaps + try { + await cliInstance.releases.uploadSourceMaps(release?.name || 'undefined', { + include: [ + { + paths: [reactRouterConfig.buildDirectory], + }, + ], + }); + } catch (error) { + // eslint-disable-next-line no-console + console.error('[Sentry] Could not upload sourcemaps', error); + } + } + // delete sourcemaps after upload + let updatedFilesToDeleteAfterUpload = sourceMapsUploadOptions?.filesToDeleteAfterUpload; + // set a default value no option was set + if (typeof sourceMapsUploadOptions?.filesToDeleteAfterUpload === 'undefined') { + updatedFilesToDeleteAfterUpload = [`${reactRouterConfig.buildDirectory}/**/*.map`]; + if (debug) { + // eslint-disable-next-line no-console + console.info( + `[Sentry] Automatically setting \`sourceMapsUploadOptions.filesToDeleteAfterUpload: ${JSON.stringify( + updatedFilesToDeleteAfterUpload, + )}\` to delete generated source maps after they were uploaded to Sentry.`, + ); + } + } + if (updatedFilesToDeleteAfterUpload) { + try { + const filePathsToDelete = await glob(updatedFilesToDeleteAfterUpload, { + absolute: true, + nodir: true, + }); + if (debug) { + filePathsToDelete.forEach(filePathToDelete => { + // eslint-disable-next-line no-console + console.info(`Deleting asset after upload: ${filePathToDelete}`); + }); + } + await Promise.all( + filePathsToDelete.map(filePathToDelete => + rm(filePathToDelete, { force: true }).catch((e: unknown) => { + if (debug) { + // This is allowed to fail - we just don't do anything + // eslint-disable-next-line no-console + console.debug(`An error occurred while attempting to delete asset: ${filePathToDelete}`, e); + } + }), + ), + ); + } catch (error) { + // eslint-disable-next-line no-console + console.error('Error deleting files after sourcemap upload:', error); + } + } +}; diff --git a/packages/react-router/src/vite/index.ts b/packages/react-router/src/vite/index.ts new file mode 100644 index 000000000000..fbe4736dbcfb --- /dev/null +++ b/packages/react-router/src/vite/index.ts @@ -0,0 +1,3 @@ +export { sentryReactRouter } from './plugin'; +export { sentryOnBuildEnd } from './buildEnd/handleOnBuildEnd'; +export type { SentryReactRouterBuildOptions } from './types'; diff --git a/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts b/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts new file mode 100644 index 000000000000..7702938f0455 --- /dev/null +++ b/packages/react-router/src/vite/makeCustomSentryVitePlugins.ts @@ -0,0 +1,37 @@ +import { sentryVitePlugin } from '@sentry/vite-plugin'; +import { type Plugin } from 'vite'; +import type { SentryReactRouterBuildOptions } from './types'; + +/** + * Create a custom subset of sentry's vite plugins + */ +export async function makeCustomSentryVitePlugins(options: SentryReactRouterBuildOptions): Promise { + const { debug, unstable_sentryVitePluginOptions, bundleSizeOptimizations, authToken, org, project, telemetry } = + options; + + const sentryVitePlugins = sentryVitePlugin({ + authToken: authToken ?? process.env.SENTRY_AUTH_TOKEN, + bundleSizeOptimizations, + debug: debug ?? false, + org: org ?? process.env.SENTRY_ORG, + project: project ?? process.env.SENTRY_PROJECT, + telemetry: telemetry ?? true, + _metaOptions: { + telemetry: { + metaFramework: 'react-router', + }, + }, + // will be handled in buildEnd hook + sourcemaps: { + disable: true, + }, + ...unstable_sentryVitePluginOptions, + }) as Plugin[]; + + // only use a subset of the plugins as all upload and file deletion tasks will be handled in the buildEnd hook + return [ + ...sentryVitePlugins.filter(plugin => { + return ['sentry-telemetry-plugin', 'sentry-vite-release-injection-plugin'].includes(plugin.name); + }), + ]; +} diff --git a/packages/react-router/src/vite/makeEnableSourceMapsPlugin.ts b/packages/react-router/src/vite/makeEnableSourceMapsPlugin.ts new file mode 100644 index 000000000000..63d1e4bd5779 --- /dev/null +++ b/packages/react-router/src/vite/makeEnableSourceMapsPlugin.ts @@ -0,0 +1,83 @@ +import { consoleSandbox } from '@sentry/core'; +import type { Plugin, UserConfig } from 'vite'; +import type { SentryReactRouterBuildOptions } from './types'; + +/** + * A Sentry plugin for React Router to enable "hidden" source maps if they are unset. + */ +export function makeEnableSourceMapsPlugin(options: SentryReactRouterBuildOptions): Plugin { + return { + name: 'sentry-react-router-update-source-map-setting', + apply: 'build', + enforce: 'post', + config(viteConfig) { + return { + ...viteConfig, + build: { + ...viteConfig.build, + sourcemap: getUpdatedSourceMapSettings(viteConfig, options), + }, + }; + }, + }; +} + +/** There are 3 ways to set up source map generation + * + * 1. User explicitly disabled source maps + * - keep this setting (emit a warning that errors won't be unminified in Sentry) + * - we won't upload anything + * + * 2. Users enabled source map generation (true, 'hidden', 'inline'). + * - keep this setting (don't do anything - like deletion - besides uploading) + * + * 3. Users didn't set source maps generation + * - we enable 'hidden' source maps generation + * - configure `filesToDeleteAfterUpload` to delete all .map files (we emit a log about this) + * + * --> only exported for testing + */ +export function getUpdatedSourceMapSettings( + viteConfig: UserConfig, + sentryPluginOptions?: SentryReactRouterBuildOptions, +): boolean | 'inline' | 'hidden' { + viteConfig.build = viteConfig.build || {}; + + const viteSourceMap = viteConfig?.build?.sourcemap; + let updatedSourceMapSetting = viteSourceMap; + + const settingKey = 'vite.build.sourcemap'; + + if (viteSourceMap === false) { + updatedSourceMapSetting = viteSourceMap; + + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.warn( + `[Sentry] Source map generation is currently disabled in your Vite configuration (\`${settingKey}: false \`). This setting is either a default setting or was explicitly set in your configuration. Sentry won't override this setting. Without source maps, code snippets on the Sentry Issues page will remain minified. To show unminified code, enable source maps in \`${settingKey}\` (e.g. by setting them to \`hidden\`).`, + ); + }); + } else if (viteSourceMap && ['hidden', 'inline', true].includes(viteSourceMap)) { + updatedSourceMapSetting = viteSourceMap; + + if (sentryPluginOptions?.debug) { + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.log( + `[Sentry] We discovered \`${settingKey}\` is set to \`${viteSourceMap.toString()}\`. Sentry will keep this source map setting. This will un-minify the code snippet on the Sentry Issue page.`, + ); + }); + } + } else { + updatedSourceMapSetting = 'hidden'; + + consoleSandbox(() => { + // eslint-disable-next-line no-console + console.log( + `[Sentry] Enabled source map generation in the build options with \`${settingKey}: 'hidden'\`. The source maps will be deleted after they were uploaded to Sentry.`, + ); + }); + } + + return updatedSourceMapSetting; +} diff --git a/packages/react-router/src/vite/plugin.ts b/packages/react-router/src/vite/plugin.ts new file mode 100644 index 000000000000..c8cdad32ee92 --- /dev/null +++ b/packages/react-router/src/vite/plugin.ts @@ -0,0 +1,26 @@ +import type { ConfigEnv } from 'vite'; +import { type Plugin } from 'vite'; +import { makeCustomSentryVitePlugins } from './makeCustomSentryVitePlugins'; +import { makeEnableSourceMapsPlugin } from './makeEnableSourceMapsPlugin'; +import type { SentryReactRouterBuildOptions } from './types'; + +/** + * A Vite plugin for Sentry that handles source map uploads and bundle size optimizations. + * + * @param options - Configuration options for the Sentry Vite plugin + * @param viteConfig - The Vite user config object + * @returns An array of Vite plugins + */ +export async function sentryReactRouter( + options: SentryReactRouterBuildOptions = {}, + config: ConfigEnv, +): Promise { + const plugins: Plugin[] = []; + + if (process.env.NODE_ENV !== 'development' && config.command === 'build' && config.mode !== 'development') { + plugins.push(makeEnableSourceMapsPlugin(options)); + plugins.push(...(await makeCustomSentryVitePlugins(options))); + } + + return plugins; +} diff --git a/packages/react-router/src/vite/types.ts b/packages/react-router/src/vite/types.ts new file mode 100644 index 000000000000..de8175b0141c --- /dev/null +++ b/packages/react-router/src/vite/types.ts @@ -0,0 +1,156 @@ +import type { SentryVitePluginOptions } from '@sentry/vite-plugin'; + +type SourceMapsOptions = { + /** + * If this flag is `true`, and an auth token is detected, the Sentry SDK will + * automatically generate and upload source maps to Sentry during a production build. + * + * @default true + */ + enabled?: boolean; + + /** + * A glob or an array of globs that specifies the build artifacts that should be deleted after the artifact + * upload to Sentry has been completed. + * + * @default [] - By default no files are deleted. + * + * The globbing patterns follow the implementation of the glob package. (https://www.npmjs.com/package/glob) + */ + filesToDeleteAfterUpload?: string | Array; + + /** + * Options related to managing the Sentry releases for a build. + * + * More info: https://docs.sentry.io/product/releases/ + */ + release?: { + /** + * Unique identifier for the release you want to create. + * + * This value can also be specified via the `SENTRY_RELEASE` environment variable. + * + * Defaults to automatically detecting a value for your environment. + * This includes values for Cordova, Heroku, AWS CodeBuild, CircleCI, Xcode, and Gradle, and otherwise uses the git `HEAD`'s commit SHA. + * (the latter requires access to git CLI and for the root directory to be a valid repository) + * + * If you didn't provide a value and the plugin can't automatically detect one, no release will be created. + */ + name?: string; + }; +}; + +type BundleSizeOptimizationOptions = { + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) any debugging code within the Sentry SDK. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * Setting this option to `true` will disable features like the SDK's `debug` option. + */ + excludeDebugStatements?: boolean; + + /** + * If set to true, the plugin will try to tree-shake tracing statements out. + * Note that the success of this depends on tree shaking generally being enabled in your build. + * Attention: DO NOT enable this when you're using any performance monitoring-related SDK features (e.g. Sentry.startSpan()). + */ + excludeTracing?: boolean; + + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay Shadow DOM recording functionality. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * This option is safe to be used when you do not want to capture any Shadow DOM activity via Sentry Session Replay. + */ + excludeReplayShadowDom?: boolean; + + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay `iframe` recording functionality. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * You can safely do this when you do not want to capture any `iframe` activity via Sentry Session Replay. + */ + excludeReplayIframe?: boolean; + + /** + * If set to `true`, the plugin will attempt to tree-shake (remove) code related to the Sentry SDK's Session Replay's Compression Web Worker. + * Note that the success of this depends on tree shaking being enabled in your build tooling. + * + * **Notice:** You should only do use this option if you manually host a compression worker and configure it in your Sentry Session Replay integration config via the `workerUrl` option. + */ + excludeReplayWorker?: boolean; +}; + +export type SentryReactRouterBuildOptions = { + /** + * Options for configuring the Sentry release. + */ + release?: { + /** + * The name of the release to create in Sentry + */ + name?: string; + }; + + /** + * The auth token to use when uploading source maps to Sentry. + * + * Instead of specifying this option, you can also set the `SENTRY_AUTH_TOKEN` environment variable. + * + * To create an auth token, follow this guide: + * @see https://docs.sentry.io/product/accounts/auth-tokens/#organization-auth-tokens + */ + authToken?: string; + + /** + * The organization slug of your Sentry organization. + * Instead of specifying this option, you can also set the `SENTRY_ORG` environment variable. + */ + org?: string; + + /** + * The project slug of your Sentry project. + * Instead of specifying this option, you can also set the `SENTRY_PROJECT` environment variable. + */ + project?: string; + + /** + * Options for the Sentry Vite plugin to customize bundle size optimizations. + */ + bundleSizeOptimizations?: BundleSizeOptimizationOptions; + + /** + * If this flag is `true`, Sentry will log debug information during build time. + * @default false. + */ + debug?: boolean; + + /** + * Options for the Sentry Vite plugin to customize the source maps upload process. + * + */ + sourceMapsUploadOptions?: SourceMapsOptions; + + /** + * If this flag is `true`, the Sentry plugin will collect some telemetry data and send it to Sentry. + * It will not collect any sensitive or user-specific data. + * + * @default true + */ + telemetry?: boolean; + + /** + * Options to further customize the Sentry Vite Plugin (@sentry/vite-plugin) behavior directly. + * Options specified in this object take precedence over the options specified in + * the `sourcemaps` and `release` objects. + * + * @see https://www.npmjs.com/package/@sentry/vite-plugin/v/2.22.2#options which lists all available options. + * + * Warning: Options within this object are subject to change at any time. + * We DO NOT guarantee semantic versioning for these options, meaning breaking + * changes can occur at any time within a major SDK version. + * + * Furthermore, some options are untested with SvelteKit specifically. Use with caution. + */ + unstable_sentryVitePluginOptions?: Partial; +}; diff --git a/packages/react-router/test/vite/buildEnd/handleOnBuildEnd.test.ts b/packages/react-router/test/vite/buildEnd/handleOnBuildEnd.test.ts new file mode 100644 index 000000000000..e183a4b1c14a --- /dev/null +++ b/packages/react-router/test/vite/buildEnd/handleOnBuildEnd.test.ts @@ -0,0 +1,237 @@ +import * as fs from 'fs'; +import SentryCli from '@sentry/cli'; +import { glob } from 'glob'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; +import { sentryOnBuildEnd } from '../../../src/vite/buildEnd/handleOnBuildEnd'; + +// Mock dependencies +vi.mock('@sentry/cli'); +vi.mock('fs', () => ({ + promises: { + rm: vi.fn().mockResolvedValue(undefined), + }, +})); +vi.mock('glob'); + +describe('sentryOnBuildEnd', () => { + const mockSentryCliInstance = { + releases: { + new: vi.fn(), + uploadSourceMaps: vi.fn(), + }, + execute: vi.fn(), + }; + + const defaultConfig = { + buildManifest: undefined, + reactRouterConfig: { + appDirectory: '/app', + basename: '/', + buildDirectory: '/build', + future: { + unstable_optimizeDeps: false, + }, + prerender: undefined, + routes: {}, + serverBuildFile: 'server.js', + serverModuleFormat: 'esm' as const, + ssr: true, + }, + viteConfig: { + build: { + sourcemap: true, + }, + sentryConfig: { + authToken: 'test-token', + org: 'test-org', + project: 'test-project', + debug: false, + }, + }, + }; + + beforeEach(() => { + vi.clearAllMocks(); + // @ts-expect-error - mocking constructor + SentryCli.mockImplementation(() => mockSentryCliInstance); + vi.mocked(glob).mockResolvedValue(['/build/file1.map', '/build/file2.map']); + vi.mocked(fs.promises.rm).mockResolvedValue(undefined); + }); + + afterEach(() => { + vi.resetModules(); + }); + + it('should create a new Sentry release when release name is provided', async () => { + const config = { + ...defaultConfig, + viteConfig: { + ...defaultConfig.viteConfig, + sentryConfig: { + ...defaultConfig.viteConfig.sentryConfig, + release: { + name: 'v1.0.0', + }, + }, + }, + }; + + await sentryOnBuildEnd(config); + + expect(mockSentryCliInstance.releases.new).toHaveBeenCalledWith('v1.0.0'); + }); + + it('should upload source maps when enabled', async () => { + const config = { + ...defaultConfig, + viteConfig: { + ...defaultConfig.viteConfig, + sentryConfig: { + ...defaultConfig.viteConfig.sentryConfig, + sourceMapsUploadOptions: { + enabled: true, + }, + }, + }, + }; + + await sentryOnBuildEnd(config); + + expect(mockSentryCliInstance.releases.uploadSourceMaps).toHaveBeenCalledWith('undefined', { + include: [{ paths: ['/build'] }], + }); + }); + + it('should not upload source maps when explicitly disabled', async () => { + const config = { + ...defaultConfig, + viteConfig: { + ...defaultConfig.viteConfig, + sentryConfig: { + ...defaultConfig.viteConfig.sentryConfig, + sourceMapsUploadOptions: { + enabled: false, + }, + }, + }, + }; + + await sentryOnBuildEnd(config); + + expect(mockSentryCliInstance.releases.uploadSourceMaps).not.toHaveBeenCalled(); + }); + + it('should delete source maps after upload with default pattern', async () => { + await sentryOnBuildEnd(defaultConfig); + + expect(glob).toHaveBeenCalledWith(['/build/**/*.map'], { + absolute: true, + nodir: true, + }); + }); + + it('should delete custom files after upload when specified', async () => { + const config = { + ...defaultConfig, + viteConfig: { + ...defaultConfig.viteConfig, + sentryConfig: { + ...defaultConfig.viteConfig.sentryConfig, + sourceMapsUploadOptions: { + filesToDeleteAfterUpload: '/custom/**/*.map', + }, + }, + }, + }; + + await sentryOnBuildEnd(config); + + expect(glob).toHaveBeenCalledWith('/custom/**/*.map', { + absolute: true, + nodir: true, + }); + }); + + it('should handle errors during release creation gracefully', async () => { + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + mockSentryCliInstance.releases.new.mockRejectedValueOnce(new Error('Release creation failed')); + + const config = { + ...defaultConfig, + viteConfig: { + ...defaultConfig.viteConfig, + sentryConfig: { + ...defaultConfig.viteConfig.sentryConfig, + release: { + name: 'v1.0.0', + }, + }, + }, + }; + + await sentryOnBuildEnd(config); + + expect(consoleSpy).toHaveBeenCalledWith('[Sentry] Could not create release', expect.any(Error)); + consoleSpy.mockRestore(); + }); + + it('should inject debug IDs before uploading source maps', async () => { + const config = { + ...defaultConfig, + viteConfig: { + ...defaultConfig.viteConfig, + sentryConfig: { + ...defaultConfig.viteConfig.sentryConfig, + sourceMapsUploadOptions: { + enabled: true, + }, + }, + }, + }; + + await sentryOnBuildEnd(config); + + expect(mockSentryCliInstance.execute).toHaveBeenCalledWith(['sourcemaps', 'inject', '/build'], false); + }); + + it('should handle errors during debug ID injection gracefully', async () => { + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + mockSentryCliInstance.execute.mockRejectedValueOnce(new Error('Injection failed')); + + await sentryOnBuildEnd(defaultConfig); + + expect(consoleSpy).toHaveBeenCalledWith('[Sentry] Could not inject debug ids', expect.any(Error)); + consoleSpy.mockRestore(); + }); + + it('should handle errors during source map upload gracefully', async () => { + const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); + mockSentryCliInstance.releases.uploadSourceMaps.mockRejectedValueOnce(new Error('Upload failed')); + + await sentryOnBuildEnd(defaultConfig); + + expect(consoleSpy).toHaveBeenCalledWith('[Sentry] Could not upload sourcemaps', expect.any(Error)); + consoleSpy.mockRestore(); + }); + + it('should log debug information when debug is enabled', async () => { + const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => {}); + + const config = { + ...defaultConfig, + viteConfig: { + ...defaultConfig.viteConfig, + sentryConfig: { + ...defaultConfig.viteConfig.sentryConfig, + debug: true, + }, + }, + }; + + await sentryOnBuildEnd(config); + + expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('[Sentry] Automatically setting')); + expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Deleting asset after upload:')); + consoleSpy.mockRestore(); + }); +}); diff --git a/packages/react-router/test/vite/plugin.test.ts b/packages/react-router/test/vite/plugin.test.ts new file mode 100644 index 000000000000..67ab338538da --- /dev/null +++ b/packages/react-router/test/vite/plugin.test.ts @@ -0,0 +1,38 @@ +import type { Plugin } from 'vite'; +import { describe, expect, it, vi } from 'vitest'; +import { sentryReactRouter } from '../../src/vite/plugin'; + +vi.spyOn(console, 'log').mockImplementation(() => { + /* noop */ +}); +vi.spyOn(console, 'warn').mockImplementation(() => { + /* noop */ +}); + +async function getSentryReactRouterVitePlugins(options?: Parameters[0]): Promise { + return sentryReactRouter( + { + project: 'project', + org: 'org', + authToken: 'token', + ...options, + }, + { + command: 'build', + mode: 'production', + }, + ); +} + +describe('sentryReactRouter()', () => { + it('returns an array of vite plugins', async () => { + const plugins = await getSentryReactRouterVitePlugins(); + expect(plugins).toBeDefined(); + const names = plugins.map(plugin => plugin.name); + expect(names).toEqual([ + 'sentry-react-router-update-source-map-setting', + 'sentry-telemetry-plugin', + 'sentry-vite-release-injection-plugin', + ]); + }); +}); diff --git a/packages/react-router/test/vite/sourceMaps.test.ts b/packages/react-router/test/vite/sourceMaps.test.ts new file mode 100644 index 000000000000..f59f7c5acf99 --- /dev/null +++ b/packages/react-router/test/vite/sourceMaps.test.ts @@ -0,0 +1,99 @@ +import type { SentryVitePluginOptions } from '@sentry/vite-plugin'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { getUpdatedSourceMapSettings, makeEnableSourceMapsPlugin } from '../../src/vite/makeEnableSourceMapsPlugin'; + +const mockedSentryVitePlugin = { + name: 'sentry-vite-debug-id-upload-plugin', + writeBundle: vi.fn(), +}; + +const sentryVitePluginSpy = vi.fn((_options: SentryVitePluginOptions) => [mockedSentryVitePlugin]); + +vi.mock('@sentry/vite-plugin', async () => { + const original = (await vi.importActual('@sentry/vite-plugin')) as any; + + return { + ...original, + sentryVitePlugin: (options: SentryVitePluginOptions) => sentryVitePluginSpy(options), + }; +}); + +beforeEach(() => { + vi.clearAllMocks(); +}); + +describe('makeEnableSourceMapsPlugin()', () => { + it('returns a plugin to set `sourcemaps` to `true`', () => { + const enableSourceMapPlugin = makeEnableSourceMapsPlugin({}); + + expect(enableSourceMapPlugin?.name).toEqual('sentry-react-router-update-source-map-setting'); + expect(enableSourceMapPlugin?.apply).toEqual('build'); + expect(enableSourceMapPlugin?.enforce).toEqual('post'); + expect(enableSourceMapPlugin?.config).toEqual(expect.any(Function)); + }); +}); + +describe('getUpdatedSourceMapSettings', () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.spyOn(console, 'warn').mockImplementation(() => {}); + vi.spyOn(console, 'log').mockImplementation(() => {}); + }); + + describe('when sourcemap is false', () => { + it('should keep sourcemap as false and show warning', () => { + const result = getUpdatedSourceMapSettings({ build: { sourcemap: false } }); + + expect(result).toBe(false); + // eslint-disable-next-line no-console + expect(console.warn).toHaveBeenCalledWith( + expect.stringContaining('[Sentry] Source map generation is currently disabled'), + ); + }); + }); + + describe('when sourcemap is explicitly set to valid values', () => { + it.each([ + ['hidden', 'hidden'], + ['inline', 'inline'], + [true, true], + ] as ('inline' | 'hidden' | boolean)[][])('should keep sourcemap as %s when set to %s', (input, expected) => { + const result = getUpdatedSourceMapSettings({ build: { sourcemap: input } }, { debug: true }); + + expect(result).toBe(expected); + // eslint-disable-next-line no-console + expect(console.log).toHaveBeenCalledWith( + expect.stringContaining(`[Sentry] We discovered \`vite.build.sourcemap\` is set to \`${input.toString()}\``), + ); + }); + }); + + describe('when sourcemap is undefined or invalid', () => { + it.each([[undefined], ['invalid'], ['something'], [null]])( + 'should set sourcemap to hidden when value is %s', + input => { + const result = getUpdatedSourceMapSettings({ build: { sourcemap: input as any } }); + + expect(result).toBe('hidden'); + // eslint-disable-next-line no-console + expect(console.log).toHaveBeenCalledWith( + expect.stringContaining( + "[Sentry] Enabled source map generation in the build options with `vite.build.sourcemap: 'hidden'`", + ), + ); + }, + ); + + it('should set sourcemap to hidden when build config is empty', () => { + const result = getUpdatedSourceMapSettings({}); + + expect(result).toBe('hidden'); + // eslint-disable-next-line no-console + expect(console.log).toHaveBeenCalledWith( + expect.stringContaining( + "[Sentry] Enabled source map generation in the build options with `vite.build.sourcemap: 'hidden'`", + ), + ); + }); + }); +}); diff --git a/packages/react/jest.config.js b/packages/react/jest.config.js deleted file mode 100644 index dec1faaf62e6..000000000000 --- a/packages/react/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -const baseConfig = require('../../jest/jest.config.js'); - -module.exports = { - ...baseConfig, - testEnvironment: 'jsdom', - // We have some tests that trigger warnings, which mess up the test logs - silent: true, -}; diff --git a/packages/react/package.json b/packages/react/package.json index 1efca9415ea2..fa1d436be43a 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -85,8 +85,8 @@ "clean": "rimraf build coverage sentry-react-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", - "test": "jest", - "test:watch": "jest --watch", + "test": "vitest run", + "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" }, "volta": { diff --git a/packages/react/src/reactrouterv6-compat-utils.tsx b/packages/react/src/reactrouterv6-compat-utils.tsx index f9cf76cfc498..492cd6c97e14 100644 --- a/packages/react/src/reactrouterv6-compat-utils.tsx +++ b/packages/react/src/reactrouterv6-compat-utils.tsx @@ -84,9 +84,7 @@ export function createV6CompatibleWrapCreateBrowserRouter< } return function (routes: RouteObject[], opts?: Record & { basename?: string }): TRouter { - routes.forEach(route => { - allRoutes.add(route); - }); + addRoutesToAllRoutes(routes); const router = createRouterFunction(routes, opts); const basename = opts?.basename; @@ -165,9 +163,7 @@ export function createV6CompatibleWrapCreateMemoryRouter< initialIndex?: number; }, ): TRouter { - routes.forEach(route => { - allRoutes.add(route); - }); + addRoutesToAllRoutes(routes); const router = createRouterFunction(routes, opts); const basename = opts?.basename; @@ -303,13 +299,7 @@ export function createV6CompatibleWrapUseRoutes(origUseRoutes: UseRoutes, versio typeof stableLocationParam === 'string' ? { pathname: stableLocationParam } : stableLocationParam; if (isMountRenderPass.current) { - routes.forEach(route => { - const extractedChildRoutes = getChildRoutesRecursively(route); - - extractedChildRoutes.forEach(r => { - allRoutes.add(r); - }); - }); + addRoutesToAllRoutes(routes); updatePageloadTransaction( getActiveRootSpan(), @@ -371,14 +361,23 @@ export function handleNavigation(opts: { [name, source] = getNormalizedName(routes, location, branches, basename); } - startBrowserTracingNavigationSpan(client, { - name, - attributes: { - [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source, - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.react.reactrouter_v${version}`, - }, - }); + const activeSpan = getActiveSpan(); + const isAlreadyInNavigationSpan = activeSpan && spanToJSON(activeSpan).op === 'navigation'; + + // Cross usage can result in multiple navigation spans being created without this check + if (isAlreadyInNavigationSpan) { + activeSpan?.updateName(name); + activeSpan?.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, source); + } else { + startBrowserTracingNavigationSpan(client, { + name, + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source, + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: `auto.navigation.react.reactrouter_v${version}`, + }, + }); + } } } @@ -450,6 +449,16 @@ function locationIsInsideDescendantRoute(location: Location, routes: RouteObject return false; } +function addRoutesToAllRoutes(routes: RouteObject[]): void { + routes.forEach(route => { + const extractedChildRoutes = getChildRoutesRecursively(route); + + extractedChildRoutes.forEach(r => { + allRoutes.add(r); + }); + }); +} + function getChildRoutesRecursively(route: RouteObject, allRoutes: Set = new Set()): Set { if (!allRoutes.has(route)) { allRoutes.add(route); @@ -458,7 +467,9 @@ function getChildRoutesRecursively(route: RouteObject, allRoutes: Set { const childRoutes = getChildRoutesRecursively(child, allRoutes); - childRoutes.forEach(r => allRoutes.add(r)); + childRoutes.forEach(r => { + allRoutes.add(r); + }); }); } } @@ -498,6 +509,10 @@ function rebuildRoutePathFromAllRoutes(allRoutes: RouteObject[], location: Locat const path = pickPath(match); const strippedPath = stripBasenameFromPathname(location.pathname, prefixWithSlash(match.pathnameBase)); + if (location.pathname === strippedPath) { + return trimSlash(strippedPath); + } + return trimSlash( trimSlash(path || '') + prefixWithSlash( @@ -588,6 +603,7 @@ function updatePageloadTransaction( if (branches) { let name, source: TransactionSource = 'url'; + const isInDescendantRoute = locationIsInsideDescendantRoute(location, allRoutes || routes); if (isInDescendantRoute) { @@ -633,13 +649,7 @@ export function createV6CompatibleWithSentryReactRouterRouting

{ - const extractedChildRoutes = getChildRoutesRecursively(route); - - extractedChildRoutes.forEach(r => { - allRoutes.add(r); - }); - }); + addRoutesToAllRoutes(routes); updatePageloadTransaction(getActiveRootSpan(), location, routes, undefined, undefined, Array.from(allRoutes)); isMountRenderPass.current = false; diff --git a/packages/react/test/error.test.ts b/packages/react/test/error.test.ts index 780c6f9657fb..8fb3cb09160b 100644 --- a/packages/react/test/error.test.ts +++ b/packages/react/test/error.test.ts @@ -1,3 +1,5 @@ +import { describe, expect, test } from 'vitest'; + import { isAtLeastReact17 } from '../src/error'; describe('isAtLeastReact17', () => { diff --git a/packages/react/test/errorboundary.test.tsx b/packages/react/test/errorboundary.test.tsx index 81f276255a96..c320bcbe353d 100644 --- a/packages/react/test/errorboundary.test.tsx +++ b/packages/react/test/errorboundary.test.tsx @@ -1,3 +1,8 @@ +/** + * @vitest-environment jsdom + */ +import { afterEach, describe, expect, it, vi } from 'vitest'; + import { Scope, getClient, setCurrentClient } from '@sentry/browser'; import type { Client } from '@sentry/core'; import { fireEvent, render, screen } from '@testing-library/react'; @@ -7,15 +12,14 @@ import { useState } from 'react'; import type { ErrorBoundaryProps, FallbackRender } from '../src/errorboundary'; import { ErrorBoundary, UNKNOWN_COMPONENT, withErrorBoundary } from '../src/errorboundary'; -const mockCaptureException = jest.fn(); -const mockShowReportDialog = jest.fn(); -const mockClientOn = jest.fn(); +const mockCaptureException = vi.fn(); +const mockShowReportDialog = vi.fn(); +const mockClientOn = vi.fn(); const EVENT_ID = 'test-id-123'; -jest.mock('@sentry/browser', () => { - const actual = jest.requireActual('@sentry/browser'); +vi.mock('@sentry/browser', async requireActual => { return { - ...actual, + ...(await requireActual()), captureException: (...args: unknown[]) => { mockCaptureException(...args); return EVENT_ID; @@ -92,7 +96,7 @@ describe('withErrorBoundary', () => { }); describe('ErrorBoundary', () => { - jest.spyOn(console, 'error').mockImplementation(); + vi.spyOn(console, 'error').mockImplementation(() => {}); afterEach(() => { mockCaptureException.mockClear(); @@ -141,7 +145,7 @@ describe('ErrorBoundary', () => { }); it('calls `onMount` when mounted', () => { - const mockOnMount = jest.fn(); + const mockOnMount = vi.fn(); render( Error Component} onMount={mockOnMount}>

children

@@ -152,7 +156,7 @@ describe('ErrorBoundary', () => { }); it('calls `onUnmount` when unmounted', () => { - const mockOnUnmount = jest.fn(); + const mockOnUnmount = vi.fn(); const { unmount } = render( Error Component} onUnmount={mockOnUnmount}>

children

@@ -243,7 +247,7 @@ describe('ErrorBoundary', () => { describe('error', () => { it('calls `componentDidCatch() when an error occurs`', () => { - const mockOnError = jest.fn(); + const mockOnError = vi.fn(); render( You have hit an error

} onError={mockOnError}>

children

@@ -267,12 +271,14 @@ describe('ErrorBoundary', () => { mechanism: { handled: true }, }); - expect(mockOnError.mock.calls[0][0]).toEqual(mockCaptureException.mock.calls[0][0]); + expect(mockOnError.mock.calls[0]?.[0]).toEqual(mockCaptureException.mock.calls[0]?.[0]); // Check if error.cause -> react component stack - const error = mockCaptureException.mock.calls[0][0]; + const error = mockCaptureException.mock.calls[0]?.[0]; const cause = error.cause; - expect(cause.stack).toEqual(mockCaptureException.mock.calls[0][1]?.captureContext.contexts.react.componentStack); + expect(cause.stack).toEqual( + mockCaptureException.mock.calls[0]?.[1]?.captureContext.contexts.react.componentStack, + ); expect(cause.name).toContain('React ErrorBoundary'); expect(cause.message).toEqual(error.message); }); @@ -326,12 +332,12 @@ describe('ErrorBoundary', () => { }); // Check if error.cause -> react component stack - const error = mockCaptureException.mock.calls[0][0]; + const error = mockCaptureException.mock.calls[0]?.[0]; expect(error.cause).not.toBeDefined(); }); it('handles when `error.cause` is nested', () => { - const mockOnError = jest.fn(); + const mockOnError = vi.fn(); function CustomBam(): JSX.Element { const firstError = new Error('bam'); @@ -364,19 +370,21 @@ describe('ErrorBoundary', () => { mechanism: { handled: true }, }); - expect(mockOnError.mock.calls[0][0]).toEqual(mockCaptureException.mock.calls[0][0]); + expect(mockOnError.mock.calls[0]?.[0]).toEqual(mockCaptureException.mock.calls[0]?.[0]); - const thirdError = mockCaptureException.mock.calls[0][0]; + const thirdError = mockCaptureException.mock.calls[0]?.[0]; const secondError = thirdError.cause; const firstError = secondError.cause; const cause = firstError.cause; - expect(cause.stack).toEqual(mockCaptureException.mock.calls[0][1]?.captureContext.contexts.react.componentStack); + expect(cause.stack).toEqual( + mockCaptureException.mock.calls[0]?.[1]?.captureContext.contexts.react.componentStack, + ); expect(cause.name).toContain('React ErrorBoundary'); expect(cause.message).toEqual(thirdError.message); }); it('handles when `error.cause` is recursive', () => { - const mockOnError = jest.fn(); + const mockOnError = vi.fn(); function CustomBam(): JSX.Element { const firstError = new Error('bam'); @@ -408,19 +416,19 @@ describe('ErrorBoundary', () => { mechanism: { handled: true }, }); - expect(mockOnError.mock.calls[0][0]).toEqual(mockCaptureException.mock.calls[0][0]); + expect(mockOnError.mock.calls[0]?.[0]).toEqual(mockCaptureException.mock.calls[0]?.[0]); - const error = mockCaptureException.mock.calls[0][0]; + const error = mockCaptureException.mock.calls[0]?.[0]; const cause = error.cause; // We need to make sure that recursive error.cause does not cause infinite loop expect(cause.stack).not.toEqual( - mockCaptureException.mock.calls[0][1]?.captureContext.contexts.react.componentStack, + mockCaptureException.mock.calls[0]?.[1]?.captureContext.contexts.react.componentStack, ); expect(cause.name).not.toContain('React ErrorBoundary'); }); it('calls `beforeCapture()` when an error occurs', () => { - const mockBeforeCapture = jest.fn(); + const mockBeforeCapture = vi.fn(); const testBeforeCapture = (...args: any[]) => { expect(mockCaptureException).toHaveBeenCalledTimes(0); @@ -516,7 +524,7 @@ describe('ErrorBoundary', () => { }); it('calls `onReset()` when reset', () => { - const mockOnReset = jest.fn(); + const mockOnReset = vi.fn(); render( ({ ...spanArgs })); -const mockFinish = jest.fn(); +const mockStartInactiveSpan = vi.fn((spanArgs: StartSpanOptions) => ({ ...spanArgs })); +const mockFinish = vi.fn(); class MockSpan extends SentrySpan { public end(): void { @@ -19,8 +23,8 @@ class MockSpan extends SentrySpan { let activeSpan: Record; -jest.mock('@sentry/browser', () => ({ - ...jest.requireActual('@sentry/browser'), +vi.mock('@sentry/browser', async requireActual => ({ + ...(await requireActual()), getActiveSpan: () => activeSpan, startInactiveSpan: (ctx: StartSpanOptions) => { mockStartInactiveSpan(ctx); diff --git a/packages/react/test/reactrouter-cross-usage.test.tsx b/packages/react/test/reactrouter-cross-usage.test.tsx new file mode 100644 index 000000000000..d55ecfb7e632 --- /dev/null +++ b/packages/react/test/reactrouter-cross-usage.test.tsx @@ -0,0 +1,803 @@ +/** + * @vitest-environment jsdom + */ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + +import { + SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, + createTransport, + getCurrentScope, + setCurrentClient, +} from '@sentry/core'; +import { render } from '@testing-library/react'; +import * as React from 'react'; +import { + MemoryRouter, + Navigate, + Route, + RouterProvider, + Routes, + createMemoryRouter, + createRoutesFromChildren, + matchRoutes, + useLocation, + useNavigationType, + useRoutes, +} from 'react-router-6'; + +import { BrowserClient } from '../src'; +import { + reactRouterV6BrowserTracingIntegration, + withSentryReactRouterV6Routing, + wrapCreateMemoryRouterV6, + wrapUseRoutesV6, +} from '../src/reactrouterv6'; + +const mockStartBrowserTracingPageLoadSpan = vi.fn(); +const mockStartBrowserTracingNavigationSpan = vi.fn(); + +const mockNavigationSpan = { + updateName: vi.fn(), + setAttribute: vi.fn(), +}; + +const mockRootSpan = { + updateName: vi.fn(), + setAttribute: vi.fn(), + getSpanJSON() { + return { op: 'pageload' }; + }, +}; + +vi.mock('@sentry/browser', async requireActual => { + const actual = (await requireActual()) as any; + return { + ...actual, + startBrowserTracingNavigationSpan: (...args: unknown[]) => { + mockStartBrowserTracingNavigationSpan(...args); + return actual.startBrowserTracingNavigationSpan(...args); + }, + startBrowserTracingPageLoadSpan: (...args: unknown[]) => { + mockStartBrowserTracingPageLoadSpan(...args); + return actual.startBrowserTracingPageLoadSpan(...args); + }, + }; +}); + +vi.mock('@sentry/core', async requireActual => { + return { + ...(await requireActual()), + getRootSpan: () => { + return mockRootSpan; + }, + }; +}); + +vi.mock('@sentry/core', async requireActual => { + const actual = (await requireActual()) as any; + return { + ...actual, + getRootSpan: () => { + return mockRootSpan; + }, + getActiveSpan: () => { + const span = actual.getActiveSpan(); + + span.updateName = mockNavigationSpan.updateName; + span.setAttribute = mockNavigationSpan.setAttribute; + + return span; + }, + }; +}); + +describe('React Router cross usage of wrappers', () => { + function createMockBrowserClient(): BrowserClient { + return new BrowserClient({ + integrations: [], + tracesSampleRate: 1, + transport: () => createTransport({ recordDroppedEvent: () => undefined }, _ => Promise.resolve({})), + stackParser: () => [], + }); + } + + beforeEach(() => { + vi.clearAllMocks(); + getCurrentScope().setClient(undefined); + }); + + describe('wrapCreateBrowserRouter and wrapUseRoutes', () => { + it('works with descendant wildcard routes - pageload', () => { + const client = createMockBrowserClient(); + setCurrentClient(client); + + client.addIntegration( + reactRouterV6BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + ); + const sentryUseRoutes = wrapUseRoutesV6(useRoutes); + + const ThirdLevel = () =>
Details
; + + const ThirdLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: '/', + element:
, + }, + { + path: ':id', + element: , + }, + ]); + + const SecondLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: 'third-level/*', + element: , + }, + { + path: '/', + element:
, + }, + { + path: '*', + element:
, + }, + ]); + + const TopLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: 'second-level/:id/*', + element: , + }, + { + path: '/', + element:
, + }, + { + path: '*', + element:
, + }, + ]); + + const createSentryMemoryRouter = wrapCreateMemoryRouterV6(createMemoryRouter); + + const router = createSentryMemoryRouter( + [ + { + children: [ + { + path: '/*', + element: , + }, + ], + }, + ], + { + initialEntries: ['/second-level/321/third-level/123'], + }, + ); + + const { container } = render( + + + , + ); + + expect(container.innerHTML).toContain('Details'); + + expect(mockStartBrowserTracingPageLoadSpan).toHaveBeenCalledTimes(1); + expect(mockRootSpan.updateName).toHaveBeenLastCalledWith('/second-level/:id/third-level/:id'); + expect(mockRootSpan.setAttribute).toHaveBeenLastCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); + }); + + it('works with descendant wildcard routes - navigation', () => { + const client = createMockBrowserClient(); + setCurrentClient(client); + + client.addIntegration( + reactRouterV6BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + ); + const sentryUseRoutes = wrapUseRoutesV6(useRoutes); + + const ThirdLevel = () =>
Details
; + + const ThirdLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: '/', + element:
, + }, + { + path: ':id', + element: , + }, + ]); + + const SecondLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: 'third-level/*', + element: , + }, + { + path: '/', + element:
, + }, + { + path: '*', + element:
, + }, + ]); + + const TopLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: 'second-level/:id/*', + element: , + }, + { + path: '*', + element:
, + }, + ]); + + const createSentryMemoryRouter = wrapCreateMemoryRouterV6(createMemoryRouter); + + const router = createSentryMemoryRouter( + [ + { + children: [ + { + path: '/*', + element: , + }, + { + path: '/navigate', + element: , + }, + ], + }, + ], + { + initialEntries: ['/navigate'], + }, + ); + + const { container } = render( + + + , + ); + + expect(container.innerHTML).toContain('Details'); + + // It's called 1 time from the wrapped `MemoryRouter` + expect(mockStartBrowserTracingNavigationSpan).toHaveBeenCalledTimes(1); + + // It's called 3 times from the 3 `useRoutes` components + expect(mockNavigationSpan.updateName).toHaveBeenCalledTimes(3); + expect(mockNavigationSpan.updateName).toHaveBeenLastCalledWith('/second-level/:id/third-level/:id'); + + expect(mockStartBrowserTracingNavigationSpan).toHaveBeenLastCalledWith(expect.any(BrowserClient), { + name: '/second-level/:id/third-level/:id', + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', + }, + }); + }); + }); + + describe('withSentryReactRouterRouting and wrapUseRoutes', () => { + it('works with descendant wildcard routes - pageload', () => { + const client = createMockBrowserClient(); + setCurrentClient(client); + + client.addIntegration( + reactRouterV6BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + ); + const sentryUseRoutes = wrapUseRoutesV6(useRoutes); + + const ThirdLevel = () =>
Details
; + + const ThirdLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: '/', + element:
, + }, + { + path: ':id', + element: , + }, + ]); + + const SecondLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: 'third-level/*', + element: , + }, + { + path: '/', + element:
, + }, + { + path: '*', + element:
, + }, + ]); + + const TopLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: 'second-level/:id/*', + element: , + }, + { + path: '/', + element:
, + }, + { + path: '*', + element:
, + }, + ]); + + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + const { container } = render( + + + + } /> + + + , + ); + + expect(container.innerHTML).toContain('Details'); + + expect(mockStartBrowserTracingPageLoadSpan).toHaveBeenCalledTimes(1); + expect(mockRootSpan.updateName).toHaveBeenLastCalledWith('/second-level/:id/third-level/:id'); + expect(mockRootSpan.setAttribute).toHaveBeenLastCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); + }); + + it('works with descendant wildcard routes - navigation', () => { + const client = createMockBrowserClient(); + setCurrentClient(client); + + client.addIntegration( + reactRouterV6BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + ); + const sentryUseRoutes = wrapUseRoutesV6(useRoutes); + + const ThirdLevel = () =>
Details
; + + const ThirdLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: '/', + element:
, + }, + { + path: ':id', + element: , + }, + ]); + + const SecondLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: 'third-level/*', + element: , + }, + { + path: '/', + element:
, + }, + { + path: '*', + element:
, + }, + ]); + + const TopLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: 'second-level/:id/*', + element: , + }, + { + path: '*', + element:
, + }, + ]); + + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + const { container } = render( + + + + } /> + } /> + + + , + ); + + expect(container.innerHTML).toContain('Details'); + + // It's called 1 time from the wrapped `MemoryRouter` + expect(mockStartBrowserTracingNavigationSpan).toHaveBeenCalledTimes(1); + + // It's called 3 times from the 3 `useRoutes` components + expect(mockNavigationSpan.updateName).toHaveBeenCalledTimes(3); + expect(mockNavigationSpan.updateName).toHaveBeenLastCalledWith('/second-level/:id/third-level/:id'); + expect(mockNavigationSpan.setAttribute).toHaveBeenLastCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); + }); + }); + + describe('withSentryReactRouterRouting and wrapCreateBrowserRouter', () => { + it('works with descendant wildcard routes - pageload', () => { + const client = createMockBrowserClient(); + setCurrentClient(client); + + client.addIntegration( + reactRouterV6BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + ); + + const createSentryMemoryRouter = wrapCreateMemoryRouterV6(createMemoryRouter); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + const ThirdLevel = () =>
Details
; + + const ThirdLevelRoutes: React.FC = () => ( + + } /> + } /> + + ); + + const SecondLevelRoutes: React.FC = () => ( + + } /> + } /> + } /> + + ); + + const TopLevelRoutes: React.FC = () => ( + + } /> + } /> + } /> + + ); + + const router = createSentryMemoryRouter( + [ + { + children: [ + { + path: '/*', + element: , + }, + ], + }, + ], + { + initialEntries: ['/second-level/321/third-level/123'], + }, + ); + + const { container } = render( + + + , + ); + + expect(container.innerHTML).toContain('Details'); + + expect(mockStartBrowserTracingPageLoadSpan).toHaveBeenCalledTimes(1); + expect(mockRootSpan.updateName).toHaveBeenLastCalledWith('/second-level/:id/third-level/:id'); + expect(mockRootSpan.setAttribute).toHaveBeenLastCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); + }); + + it('works with descendant wildcard routes - navigation', () => { + const client = createMockBrowserClient(); + setCurrentClient(client); + + client.addIntegration( + reactRouterV6BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + ); + + const createSentryMemoryRouter = wrapCreateMemoryRouterV6(createMemoryRouter); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + const ThirdLevel = () =>
Details
; + + const ThirdLevelRoutes: React.FC = () => ( + + } /> + } /> + + ); + + const SecondLevelRoutes: React.FC = () => ( + + } /> + } /> + } /> + + ); + + const TopLevelRoutes: React.FC = () => ( + + } /> + } /> + + ); + + const router = createSentryMemoryRouter( + [ + { + children: [ + { + path: '/*', + element: , + }, + { + path: '/navigate', + element: , + }, + ], + }, + ], + { + initialEntries: ['/navigate'], + }, + ); + + const { container } = render( + + + , + ); + + expect(container.innerHTML).toContain('Details'); + + // It's called 1 time from the wrapped `createMemoryRouter` + expect(mockStartBrowserTracingNavigationSpan).toHaveBeenCalledTimes(1); + + // It's called 3 times from the 3 `SentryRoutes` components + expect(mockNavigationSpan.updateName).toHaveBeenCalledTimes(3); + expect(mockNavigationSpan.updateName).toHaveBeenLastCalledWith('/second-level/:id/third-level/:id'); + + expect(mockStartBrowserTracingNavigationSpan).toHaveBeenLastCalledWith(expect.any(BrowserClient), { + name: '/second-level/:id/third-level/:id', + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'navigation', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.navigation.react.reactrouter_v6', + }, + }); + }); + }); + + describe('withSentryReactRouterRouting and wrapUseRoutes and wrapCreateBrowserRouter', () => { + it('works with descendant wildcard routes - pageload', () => { + const client = createMockBrowserClient(); + setCurrentClient(client); + + client.addIntegration( + reactRouterV6BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + ); + const sentryUseRoutes = wrapUseRoutesV6(useRoutes); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + const ThirdLevel = () =>
Details
; + + const ThirdLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: '/', + element:
, + }, + { + path: ':id', + element: , + }, + ]); + + const SecondLevelRoutes: React.FC = () => ( + + } /> + } /> + } /> + + ); + + const TopLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: 'second-level/:id/*', + element: , + }, + { + path: '*', + element:
, + }, + ]); + + const createSentryMemoryRouter = wrapCreateMemoryRouterV6(createMemoryRouter); + + const router = createSentryMemoryRouter( + [ + { + children: [ + { + path: '/*', + element: , + }, + ], + }, + ], + { + initialEntries: ['/second-level/321/third-level/123'], + }, + ); + + const { container } = render( + + + , + ); + + expect(container.innerHTML).toContain('Details'); + + expect(mockStartBrowserTracingPageLoadSpan).toHaveBeenCalledTimes(1); + expect(mockRootSpan.updateName).toHaveBeenLastCalledWith('/second-level/:id/third-level/:id'); + expect(mockRootSpan.setAttribute).toHaveBeenLastCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); + }); + + it('works with descendant wildcard routes - navigation', () => { + const client = createMockBrowserClient(); + setCurrentClient(client); + + client.addIntegration( + reactRouterV6BrowserTracingIntegration({ + useEffect: React.useEffect, + useLocation, + useNavigationType, + createRoutesFromChildren, + matchRoutes, + }), + ); + const sentryUseRoutes = wrapUseRoutesV6(useRoutes); + const SentryRoutes = withSentryReactRouterV6Routing(Routes); + + const ThirdLevel = () =>
Details
; + + const ThirdLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: '/', + element:
, + }, + { + path: ':id', + element: , + }, + ]); + + const SecondLevelRoutes: React.FC = () => ( + + } /> + } /> + } /> + + ); + + const TopLevelRoutes: React.FC = () => + sentryUseRoutes([ + { + path: 'second-level/:id/*', + element: , + }, + { + path: '*', + element:
, + }, + ]); + + const createSentryMemoryRouter = wrapCreateMemoryRouterV6(createMemoryRouter); + + const router = createSentryMemoryRouter( + [ + { + children: [ + { + path: '/*', + element: , + }, + { + path: '/navigate', + element: , + }, + ], + }, + ], + { + initialEntries: ['/navigate'], + }, + ); + + const { container } = render( + + + , + ); + + expect(container.innerHTML).toContain('Details'); + + // It's called 1 time from the wrapped `MemoryRouter` + expect(mockStartBrowserTracingNavigationSpan).toHaveBeenCalledTimes(1); + + // It's called 3 times from the 2 `useRoutes` components and 1 component + expect(mockNavigationSpan.updateName).toHaveBeenCalledTimes(3); + + expect(mockNavigationSpan.updateName).toHaveBeenLastCalledWith('/second-level/:id/third-level/:id'); + expect(mockNavigationSpan.setAttribute).toHaveBeenLastCalledWith(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route'); + }); + }); +}); diff --git a/packages/react/test/reactrouter-descendant-routes.test.tsx b/packages/react/test/reactrouter-descendant-routes.test.tsx index dcc73a2275df..ff89ae86f639 100644 --- a/packages/react/test/reactrouter-descendant-routes.test.tsx +++ b/packages/react/test/reactrouter-descendant-routes.test.tsx @@ -1,3 +1,8 @@ +/** + * @vitest-environment jsdom + */ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, @@ -28,19 +33,19 @@ import { wrapUseRoutesV6, } from '../src/reactrouterv6'; -const mockStartBrowserTracingPageLoadSpan = jest.fn(); -const mockStartBrowserTracingNavigationSpan = jest.fn(); +const mockStartBrowserTracingPageLoadSpan = vi.fn(); +const mockStartBrowserTracingNavigationSpan = vi.fn(); const mockRootSpan = { - updateName: jest.fn(), - setAttribute: jest.fn(), + updateName: vi.fn(), + setAttribute: vi.fn(), getSpanJSON() { return { op: 'pageload' }; }, }; -jest.mock('@sentry/browser', () => { - const actual = jest.requireActual('@sentry/browser'); +vi.mock('@sentry/browser', async requireActual => { + const actual = (await requireActual()) as any; return { ...actual, startBrowserTracingNavigationSpan: (...args: unknown[]) => { @@ -54,10 +59,9 @@ jest.mock('@sentry/browser', () => { }; }); -jest.mock('@sentry/core', () => { - const actual = jest.requireActual('@sentry/core'); +vi.mock('@sentry/core', async requireActual => { return { - ...actual, + ...(await requireActual()), getRootSpan: () => { return mockRootSpan; }, @@ -75,7 +79,7 @@ describe('React Router Descendant Routes', () => { } beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); getCurrentScope().setClient(undefined); }); diff --git a/packages/react/test/reactrouterv3.test.tsx b/packages/react/test/reactrouterv3.test.tsx index cf8995630e71..c89158641e62 100644 --- a/packages/react/test/reactrouterv3.test.tsx +++ b/packages/react/test/reactrouterv3.test.tsx @@ -1,3 +1,8 @@ +/** + * @vitest-environment jsdom + */ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + import { BrowserClient } from '@sentry/browser'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, @@ -8,23 +13,22 @@ import { setCurrentClient, } from '@sentry/core'; import { act, render } from '@testing-library/react'; -// biome-ignore lint/nursery/noUnusedImports: import * as React from 'react'; import { IndexRoute, Route, Router, createMemoryHistory, createRoutes, match } from 'react-router-3'; import { reactRouterV3BrowserTracingIntegration } from '../src/reactrouterv3'; -const mockStartBrowserTracingPageLoadSpan = jest.fn(); -const mockStartBrowserTracingNavigationSpan = jest.fn(); +const mockStartBrowserTracingPageLoadSpan = vi.fn(); +const mockStartBrowserTracingNavigationSpan = vi.fn(); const mockRootSpan = { - setAttribute: jest.fn(), + setAttribute: vi.fn(), getSpanJSON() { return { op: 'pageload' }; }, }; -jest.mock('@sentry/browser', () => { - const actual = jest.requireActual('@sentry/browser'); +vi.mock('@sentry/browser', async requireActual => { + const actual = (await requireActual()) as any; return { ...actual, startBrowserTracingNavigationSpan: (...args: unknown[]) => { @@ -38,10 +42,9 @@ jest.mock('@sentry/browser', () => { }; }); -jest.mock('@sentry/core', () => { - const actual = jest.requireActual('@sentry/core'); +vi.mock('@sentry/core', async requireActual => { return { - ...actual, + ...(await requireActual()), getRootSpan: () => { return mockRootSpan; }, @@ -78,7 +81,7 @@ describe('browserTracingReactRouterV3', () => { } beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); getCurrentScope().setClient(undefined); }); diff --git a/packages/react/test/reactrouterv4.test.tsx b/packages/react/test/reactrouterv4.test.tsx index cb479ed57d83..8d4098946221 100644 --- a/packages/react/test/reactrouterv4.test.tsx +++ b/packages/react/test/reactrouterv4.test.tsx @@ -1,3 +1,8 @@ +/** + * @vitest-environment jsdom + */ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, @@ -8,26 +13,25 @@ import { } from '@sentry/core'; import { act, render } from '@testing-library/react'; import { createMemoryHistory } from 'history-4'; -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Route, Router, Switch, matchPath } from 'react-router-4'; import { BrowserClient, reactRouterV4BrowserTracingIntegration, withSentryRouting } from '../src'; import type { RouteConfig } from '../src/reactrouter'; -const mockStartBrowserTracingPageLoadSpan = jest.fn(); -const mockStartBrowserTracingNavigationSpan = jest.fn(); +const mockStartBrowserTracingPageLoadSpan = vi.fn(); +const mockStartBrowserTracingNavigationSpan = vi.fn(); const mockRootSpan = { - updateName: jest.fn(), - setAttribute: jest.fn(), + updateName: vi.fn(), + setAttribute: vi.fn(), getSpanJSON() { return { op: 'pageload' }; }, }; -jest.mock('@sentry/browser', () => { - const actual = jest.requireActual('@sentry/browser'); +vi.mock('@sentry/browser', async requireActual => { + const actual = (await requireActual()) as any; return { ...actual, startBrowserTracingNavigationSpan: (...args: unknown[]) => { @@ -41,10 +45,9 @@ jest.mock('@sentry/browser', () => { }; }); -jest.mock('@sentry/core', () => { - const actual = jest.requireActual('@sentry/core'); +vi.mock('@sentry/core', async requireActual => { return { - ...actual, + ...(await requireActual()), getRootSpan: () => { return mockRootSpan; }, @@ -62,7 +65,7 @@ describe('browserTracingReactRouterV4', () => { } beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); getCurrentScope().setClient(undefined); }); diff --git a/packages/react/test/reactrouterv5.test.tsx b/packages/react/test/reactrouterv5.test.tsx index 45271fea7bee..d8fe00d8360b 100644 --- a/packages/react/test/reactrouterv5.test.tsx +++ b/packages/react/test/reactrouterv5.test.tsx @@ -1,3 +1,8 @@ +/** + * @vitest-environment jsdom + */ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, @@ -8,26 +13,25 @@ import { } from '@sentry/core'; import { act, render } from '@testing-library/react'; import { createMemoryHistory } from 'history-4'; -// biome-ignore lint/nursery/noUnusedImports: Need React import for JSX import * as React from 'react'; import { Route, Router, Switch, matchPath } from 'react-router-5'; import { BrowserClient, reactRouterV5BrowserTracingIntegration, withSentryRouting } from '../src'; import type { RouteConfig } from '../src/reactrouter'; -const mockStartBrowserTracingPageLoadSpan = jest.fn(); -const mockStartBrowserTracingNavigationSpan = jest.fn(); +const mockStartBrowserTracingPageLoadSpan = vi.fn(); +const mockStartBrowserTracingNavigationSpan = vi.fn(); const mockRootSpan = { - updateName: jest.fn(), - setAttribute: jest.fn(), + updateName: vi.fn(), + setAttribute: vi.fn(), getSpanJSON() { return { op: 'pageload' }; }, }; -jest.mock('@sentry/browser', () => { - const actual = jest.requireActual('@sentry/browser'); +vi.mock('@sentry/browser', async requireActual => { + const actual = (await requireActual()) as any; return { ...actual, startBrowserTracingNavigationSpan: (...args: unknown[]) => { @@ -41,10 +45,9 @@ jest.mock('@sentry/browser', () => { }; }); -jest.mock('@sentry/core', () => { - const actual = jest.requireActual('@sentry/core'); +vi.mock('@sentry/core', async requireActual => { return { - ...actual, + ...(await requireActual()), getRootSpan: () => { return mockRootSpan; }, @@ -62,7 +65,7 @@ describe('browserTracingReactRouterV5', () => { } beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); getCurrentScope().setClient(undefined); }); diff --git a/packages/react/test/reactrouterv6-compat-utils.test.tsx b/packages/react/test/reactrouterv6-compat-utils.test.tsx index 2bbe1ec7e52c..ee07da4dafe1 100644 --- a/packages/react/test/reactrouterv6-compat-utils.test.tsx +++ b/packages/react/test/reactrouterv6-compat-utils.test.tsx @@ -1,3 +1,5 @@ +import { describe, expect } from 'vitest'; + import { getNumberOfUrlSegments } from '../src/reactrouterv6-compat-utils'; describe('getNumberOfUrlSegments', () => { diff --git a/packages/react/test/reactrouterv6.test.tsx b/packages/react/test/reactrouterv6.test.tsx index 879abf15d2d0..77777e527a9b 100644 --- a/packages/react/test/reactrouterv6.test.tsx +++ b/packages/react/test/reactrouterv6.test.tsx @@ -1,3 +1,8 @@ +/** + * @vitest-environment jsdom + */ +import { beforeEach, describe, expect, it, vi } from 'vitest'; + import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, @@ -33,19 +38,19 @@ import { wrapUseRoutesV6, } from '../src/reactrouterv6'; -const mockStartBrowserTracingPageLoadSpan = jest.fn(); -const mockStartBrowserTracingNavigationSpan = jest.fn(); +const mockStartBrowserTracingPageLoadSpan = vi.fn(); +const mockStartBrowserTracingNavigationSpan = vi.fn(); const mockRootSpan = { - updateName: jest.fn(), - setAttribute: jest.fn(), + updateName: vi.fn(), + setAttribute: vi.fn(), getSpanJSON() { return { op: 'pageload' }; }, }; -jest.mock('@sentry/browser', () => { - const actual = jest.requireActual('@sentry/browser'); +vi.mock('@sentry/browser', async requireActual => { + const actual = (await requireActual()) as any; return { ...actual, startBrowserTracingNavigationSpan: (...args: unknown[]) => { @@ -59,10 +64,9 @@ jest.mock('@sentry/browser', () => { }; }); -jest.mock('@sentry/core', () => { - const actual = jest.requireActual('@sentry/core'); +vi.mock('@sentry/core', async requireActual => { return { - ...actual, + ...(await requireActual()), getRootSpan: () => { return mockRootSpan; }, @@ -80,7 +84,7 @@ describe('reactRouterV6BrowserTracingIntegration', () => { } beforeEach(() => { - jest.clearAllMocks(); + vi.clearAllMocks(); getCurrentScope().setClient(undefined); }); diff --git a/packages/react/test/redux.test.ts b/packages/react/test/redux.test.ts index 50116c8db87e..b08e8a0061bc 100644 --- a/packages/react/test/redux.test.ts +++ b/packages/react/test/redux.test.ts @@ -1,14 +1,17 @@ +import type { Mock } from 'vitest'; +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; + import * as Sentry from '@sentry/browser'; import * as SentryCore from '@sentry/core'; import * as Redux from 'redux'; import { createReduxEnhancer } from '../src/redux'; -const mockSetContext = jest.fn(); -const mockGlobalScopeAddEventProcessor = jest.fn(); +const mockSetContext = vi.fn(); +const mockGlobalScopeAddEventProcessor = vi.fn(); -jest.mock('@sentry/core', () => ({ - ...jest.requireActual('@sentry/core'), +vi.mock('@sentry/core', async requireActual => ({ + ...(await requireActual()), getCurrentScope() { return { setContext: mockSetContext, @@ -19,8 +22,8 @@ jest.mock('@sentry/core', () => ({ addEventProcessor: mockGlobalScopeAddEventProcessor, }; }, - addEventProcessor: jest.fn(), - addBreadcrumb: jest.fn(), + addEventProcessor: vi.fn(), + addBreadcrumb: vi.fn(), })); afterEach(() => { @@ -29,10 +32,10 @@ afterEach(() => { }); describe('createReduxEnhancer', () => { - let mockAddBreadcrumb: jest.SpyInstance; + let mockAddBreadcrumb: Mock; beforeEach(() => { - mockAddBreadcrumb = SentryCore.addBreadcrumb as unknown as jest.SpyInstance; + mockAddBreadcrumb = SentryCore.addBreadcrumb as unknown as Mock; mockAddBreadcrumb.mockReset(); }); @@ -221,7 +224,7 @@ describe('createReduxEnhancer', () => { }); it('configureScopeWithState is passed latest state', () => { - const configureScopeWithState = jest.fn(); + const configureScopeWithState = vi.fn(); const enhancer = createReduxEnhancer({ configureScopeWithState, }); @@ -269,7 +272,7 @@ describe('createReduxEnhancer', () => { expect(mockGlobalScopeAddEventProcessor).toHaveBeenCalledTimes(1); - const callbackFunction = mockGlobalScopeAddEventProcessor.mock.calls[0][0]; + const callbackFunction = mockGlobalScopeAddEventProcessor.mock.calls[0]?.[0]; const mockEvent = { contexts: { @@ -330,7 +333,7 @@ describe('createReduxEnhancer', () => { expect(mockGlobalScopeAddEventProcessor).toHaveBeenCalledTimes(1); - const callbackFunction = mockGlobalScopeAddEventProcessor.mock.calls[0][0]; + const callbackFunction = mockGlobalScopeAddEventProcessor.mock.calls[0]?.[0]; const mockEvent = { contexts: { @@ -365,7 +368,7 @@ describe('createReduxEnhancer', () => { expect(mockGlobalScopeAddEventProcessor).toHaveBeenCalledTimes(1); - const callbackFunction = mockGlobalScopeAddEventProcessor.mock.calls[0][0]; + const callbackFunction = mockGlobalScopeAddEventProcessor.mock.calls[0]?.[0]; const mockEvent = { contexts: { @@ -397,7 +400,7 @@ describe('createReduxEnhancer', () => { expect(mockGlobalScopeAddEventProcessor).toHaveBeenCalledTimes(1); - const callbackFunction = mockGlobalScopeAddEventProcessor.mock.calls[0][0]; + const callbackFunction = mockGlobalScopeAddEventProcessor.mock.calls[0]?.[0]; const mockEvent = { type: 'not_redux', diff --git a/packages/react/test/sdk.test.ts b/packages/react/test/sdk.test.ts index 825ade6f0b25..fde2c71b2797 100644 --- a/packages/react/test/sdk.test.ts +++ b/packages/react/test/sdk.test.ts @@ -1,17 +1,12 @@ +import { describe, expect, it, vi } from 'vitest'; + import * as SentryBrowser from '@sentry/browser'; import { version } from 'react'; import { init } from '../src/sdk'; -jest.mock('@sentry/browser', () => { - return { - __esModule: true, - ...jest.requireActual('@sentry/browser'), - }; -}); - describe('init', () => { it('sets the React version (if available) in the global scope', () => { - const setContextSpy = jest.spyOn(SentryBrowser, 'setContext'); + const setContextSpy = vi.spyOn(SentryBrowser, 'setContext'); init({}); diff --git a/packages/react/tsconfig.test.json b/packages/react/tsconfig.test.json index af7e36ec0eda..00cada2d8bcf 100644 --- a/packages/react/tsconfig.test.json +++ b/packages/react/tsconfig.test.json @@ -1,12 +1,9 @@ { "extends": "./tsconfig.json", - "include": ["test/**/*"], + "include": ["test/**/*", "vite.config.ts"], "compilerOptions": { - // should include all types from `./tsconfig.json` plus types for all test frameworks used - "types": ["jest"] - // other package-specific, test-specific options } } diff --git a/packages/react/vite.config.ts b/packages/react/vite.config.ts new file mode 100644 index 000000000000..199c74df98ce --- /dev/null +++ b/packages/react/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vitest/config'; + +import baseConfig from '../../vite/vite.config'; + +export default defineConfig({ + ...baseConfig, + test: { + globals: true, + ...baseConfig.test, + }, +}); diff --git a/packages/remix/package.json b/packages/remix/package.json index c0abdcb0ac91..4470b2c9ee6a 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -30,6 +30,12 @@ }, "node": "./build/cjs/index.server.js" }, + "./cloudflare": { + "import": "./build/esm/cloudflare/index.js", + "require": "./build/cjs/cloudflare/index.js", + "types": "./build/types/cloudflare/index.types.d.ts", + "default": "./build/esm/cloudflare/index.js" + }, "./import": { "import": { "default": "./build/import-hook.mjs" @@ -86,7 +92,7 @@ "build:transpile:watch": "rollup -c rollup.npm.config.mjs --watch", "build:types:watch": "tsc -p tsconfig.types.json --watch", "build:tarball": "npm pack", - "circularDepCheck": "madge --circular src/index.server.ts", + "circularDepCheck": "madge --circular src/index.server.ts && madge --circular src/index.client.ts", "clean": "rimraf build coverage sentry-remix-*.tgz", "fix": "eslint . --format stylish --fix", "lint": "eslint . --format stylish", @@ -105,8 +111,5 @@ "volta": { "extends": "../../package.json" }, - "sideEffects": [ - "./esm/index.server.js", - "./src/index.server.ts" - ] + "sideEffects": false } diff --git a/packages/remix/rollup.npm.config.mjs b/packages/remix/rollup.npm.config.mjs index 346e043eb0f9..8ba7eac8051b 100644 --- a/packages/remix/rollup.npm.config.mjs +++ b/packages/remix/rollup.npm.config.mjs @@ -3,7 +3,13 @@ import { makeBaseNPMConfig, makeNPMConfigVariants, makeOtelLoaders } from '@sent export default [ ...makeNPMConfigVariants( makeBaseNPMConfig({ - entrypoints: ['src/index.server.ts', 'src/index.client.tsx'], + entrypoints: [ + 'src/index.server.ts', + 'src/index.client.ts', + 'src/client/index.ts', + 'src/server/index.ts', + 'src/cloudflare/index.ts', + ], packageSpecificConfig: { external: ['react-router', 'react-router-dom', 'react', 'react/jsx-runtime'], output: { diff --git a/packages/remix/src/client/index.ts b/packages/remix/src/client/index.ts new file mode 100644 index 000000000000..fc77db42ad3d --- /dev/null +++ b/packages/remix/src/client/index.ts @@ -0,0 +1,24 @@ +import { logger } from '@sentry/core'; +import { DEBUG_BUILD } from '../utils/debug-build'; + +export * from '@sentry/react'; + +export { init } from './sdk'; +export { captureRemixErrorBoundaryError } from './errors'; +export { withSentry } from './performance'; +export { browserTracingIntegration } from './browserTracingIntegration'; + +// This is a no-op function that does nothing. It's here to make sure that the +// function signature is the same as in the server SDK. +// See issue: https://github.com/getsentry/sentry-javascript/issues/9594 +/* eslint-disable @typescript-eslint/no-unused-vars */ +/** + * + */ +export async function captureRemixServerException(err: unknown, name: string, request: Request): Promise { + DEBUG_BUILD && + logger.warn( + '`captureRemixServerException` is a server-only function and should not be called in the browser. ' + + 'This function is a no-op in the browser environment.', + ); +} diff --git a/packages/remix/src/client/performance.tsx b/packages/remix/src/client/performance.tsx index 90520381dfcd..9d47aae36d4a 100644 --- a/packages/remix/src/client/performance.tsx +++ b/packages/remix/src/client/performance.tsx @@ -103,8 +103,16 @@ function startNavigationSpan(matches: RouteMatch[]): void { * @param OrigApp The Remix root to wrap * @param options The options for ErrorBoundary wrapper. */ -export function withSentry

, R extends React.ComponentType

>(OrigApp: R): R { +export function withSentry

, R extends React.ComponentType

>( + OrigApp: R, + useEffect?: UseEffect, + useLocation?: UseLocation, + useMatches?: UseMatches, + instrumentNavigation?: boolean, +): R { const SentryRoot: React.FC

= (props: P) => { + setGlobals({ useEffect, useLocation, useMatches, instrumentNavigation: instrumentNavigation || true }); + // Early return when any of the required functions is not available. if (!_useEffect || !_useLocation || !_useMatches) { DEBUG_BUILD && @@ -184,8 +192,8 @@ export function setGlobals({ useMatches?: UseMatches; instrumentNavigation?: boolean; }): void { - _useEffect = useEffect; - _useLocation = useLocation; - _useMatches = useMatches; - _instrumentNavigation = instrumentNavigation; + _useEffect = useEffect || _useEffect; + _useLocation = useLocation || _useLocation; + _useMatches = useMatches || _useMatches; + _instrumentNavigation = instrumentNavigation ?? _instrumentNavigation; } diff --git a/packages/remix/src/client/sdk.ts b/packages/remix/src/client/sdk.ts new file mode 100644 index 000000000000..21b19e1aeb24 --- /dev/null +++ b/packages/remix/src/client/sdk.ts @@ -0,0 +1,21 @@ +/* eslint-enable @typescript-eslint/no-unused-vars */ +import type { Client } from '@sentry/core'; +import { applySdkMetadata } from '@sentry/core'; +import { init as reactInit } from '@sentry/react'; +import type { RemixOptions } from '../utils/remixOptions'; + +/** + * Initializes the Remix SDK. + * @param options The configuration options. + * @returns The initialized SDK. + */ +export function init(options: RemixOptions): Client | undefined { + const opts = { + ...options, + environment: options.environment || process.env.NODE_ENV, + }; + + applySdkMetadata(opts, 'remix', ['remix', 'react']); + + return reactInit(opts); +} diff --git a/packages/remix/src/cloudflare/index.ts b/packages/remix/src/cloudflare/index.ts new file mode 100644 index 000000000000..4634baa1381e --- /dev/null +++ b/packages/remix/src/cloudflare/index.ts @@ -0,0 +1,104 @@ +export * from '@sentry/react'; + +export { captureRemixErrorBoundaryError } from '../client/errors'; +export { withSentry } from '../client/performance'; + +import { instrumentBuild as instrumentRemixBuild, makeWrappedCreateRequestHandler } from '../server/instrumentServer'; +export { makeWrappedCreateRequestHandler }; + +/** + * Instruments a Remix build to capture errors and performance data. + * @param build The Remix build to instrument. + * @returns The instrumented Remix build. + */ +export const instrumentBuild: typeof instrumentRemixBuild = build => { + return instrumentRemixBuild(build, { + instrumentTracing: true, + }); +}; + +export type { + Breadcrumb, + BreadcrumbHint, + PolymorphicRequest, + RequestEventData, + SdkInfo, + Event, + EventHint, + ErrorEvent, + Exception, + Session, + SeverityLevel, + Span, + StackFrame, + Stacktrace, + Thread, + User, +} from '@sentry/core'; + +export { + addEventProcessor, + addBreadcrumb, + addIntegration, + captureException, + captureEvent, + captureMessage, + captureFeedback, + close, + createTransport, + lastEventId, + flush, + getClient, + isInitialized, + getCurrentScope, + getGlobalScope, + getIsolationScope, + setCurrentClient, + Scope, + SDK_VERSION, + setContext, + setExtra, + setExtras, + setTag, + setTags, + setUser, + getSpanStatusFromHttpCode, + setHttpStatus, + withScope, + withIsolationScope, + captureCheckIn, + withMonitor, + setMeasurement, + getActiveSpan, + getRootSpan, + getTraceData, + getTraceMetaTags, + startSpan, + startInactiveSpan, + startSpanManual, + startNewTrace, + suppressTracing, + withActiveSpan, + getSpanDescendants, + continueTrace, + functionToStringIntegration, + // eslint-disable-next-line deprecation/deprecation + inboundFiltersIntegration, + linkedErrorsIntegration, + requestDataIntegration, + extraErrorDataIntegration, + dedupeIntegration, + rewriteFramesIntegration, + captureConsoleIntegration, + moduleMetadataIntegration, + zodErrorsIntegration, + SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, + SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + trpcMiddleware, + spanToJSON, + spanToTraceHeader, + spanToBaggageHeader, + updateSpanName, +} from '@sentry/core'; diff --git a/packages/remix/src/index.client.ts b/packages/remix/src/index.client.ts new file mode 100644 index 000000000000..4f1cce44fa36 --- /dev/null +++ b/packages/remix/src/index.client.ts @@ -0,0 +1 @@ +export * from './client'; diff --git a/packages/remix/src/index.client.tsx b/packages/remix/src/index.client.tsx deleted file mode 100644 index 3f6d14294978..000000000000 --- a/packages/remix/src/index.client.tsx +++ /dev/null @@ -1,35 +0,0 @@ -/* eslint-enable @typescript-eslint/no-unused-vars */ - -import type { Client } from '@sentry/core'; -import { applySdkMetadata, logger } from '@sentry/core'; -import { init as reactInit } from '@sentry/react'; -import { DEBUG_BUILD } from './utils/debug-build'; -import type { RemixOptions } from './utils/remixOptions'; - -export { browserTracingIntegration } from './client/browserTracingIntegration'; -export { captureRemixErrorBoundaryError } from './client/errors'; -export { withSentry } from './client/performance'; -export * from '@sentry/react'; - -// This is a no-op function that does nothing. It's here to make sure that the -// function signature is the same as in the server SDK. -// See issue: https://github.com/getsentry/sentry-javascript/issues/9594 -/* eslint-disable @typescript-eslint/no-unused-vars */ -export async function captureRemixServerException(err: unknown, name: string, request: Request): Promise { - DEBUG_BUILD && - logger.warn( - '`captureRemixServerException` is a server-only function and should not be called in the browser. ' + - 'This function is a no-op in the browser environment.', - ); -} - -export function init(options: RemixOptions): Client | undefined { - const opts = { - ...options, - environment: options.environment || process.env.NODE_ENV, - }; - - applySdkMetadata(opts, 'remix', ['remix', 'react']); - - return reactInit(opts); -} diff --git a/packages/remix/src/index.server.ts b/packages/remix/src/index.server.ts index 9084284217a9..9d9fd47efd47 100644 --- a/packages/remix/src/index.server.ts +++ b/packages/remix/src/index.server.ts @@ -1,173 +1,4 @@ -import { applySdkMetadata, logger } from '@sentry/core'; -import type { Integration } from '@sentry/core'; -import type { NodeClient, NodeOptions } from '@sentry/node'; -import { getDefaultIntegrations as getDefaultNodeIntegrations, init as nodeInit, isInitialized } from '@sentry/node'; - -import { DEBUG_BUILD } from './utils/debug-build'; -import { instrumentServer } from './utils/instrumentServer'; -import { httpIntegration } from './utils/integrations/http'; -import { remixIntegration } from './utils/integrations/opentelemetry'; -import type { RemixOptions } from './utils/remixOptions'; - -// We need to explicitly export @sentry/node as they end up under `default` in ESM builds -// See: https://github.com/getsentry/sentry-javascript/issues/8474 -export { - addBreadcrumb, - addEventProcessor, - addIntegration, - amqplibIntegration, - anrIntegration, - disableAnrDetectionForCallback, - captureCheckIn, - captureConsoleIntegration, - captureEvent, - captureException, - captureFeedback, - captureMessage, - captureSession, - close, - connectIntegration, - consoleIntegration, - contextLinesIntegration, - continueTrace, - createGetModuleFromFilename, - createTransport, - cron, - dedupeIntegration, - defaultStackParser, - endSession, - expressErrorHandler, - expressIntegration, - extraErrorDataIntegration, - fastifyIntegration, - flush, - functionToStringIntegration, - generateInstrumentOnce, - genericPoolIntegration, - getActiveSpan, - getAutoPerformanceIntegrations, - getClient, - getCurrentScope, - getDefaultIntegrations, - getGlobalScope, - getIsolationScope, - getRootSpan, - getSentryRelease, - getSpanDescendants, - getSpanStatusFromHttpCode, - graphqlIntegration, - hapiIntegration, - httpIntegration, - inboundFiltersIntegration, - initOpenTelemetry, - isInitialized, - knexIntegration, - kafkaIntegration, - koaIntegration, - lastEventId, - linkedErrorsIntegration, - localVariablesIntegration, - makeNodeTransport, - modulesIntegration, - mongoIntegration, - mongooseIntegration, - mysql2Integration, - mysqlIntegration, - nativeNodeFetchIntegration, - NodeClient, - nodeContextIntegration, - onUncaughtExceptionIntegration, - onUnhandledRejectionIntegration, - parameterize, - postgresIntegration, - prismaIntegration, - redisIntegration, - requestDataIntegration, - rewriteFramesIntegration, - Scope, - SDK_VERSION, - SEMANTIC_ATTRIBUTE_SENTRY_OP, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, - SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, - setContext, - setCurrentClient, - setExtra, - setExtras, - setHttpStatus, - setMeasurement, - setTag, - setTags, - setupConnectErrorHandler, - setupExpressErrorHandler, - setupHapiErrorHandler, - setupKoaErrorHandler, - setUser, - spanToBaggageHeader, - spanToJSON, - spanToTraceHeader, - spotlightIntegration, - startInactiveSpan, - startNewTrace, - suppressTracing, - startSession, - startSpan, - startSpanManual, - tediousIntegration, - trpcMiddleware, - updateSpanName, - withActiveSpan, - withIsolationScope, - withMonitor, - withScope, - zodErrorsIntegration, -} from '@sentry/node'; - -// Keeping the `*` exports for backwards compatibility and types -export * from '@sentry/node'; - -export { - sentryHandleError, - wrapHandleErrorWithSentry, -} from './utils/instrumentServer'; - -export { captureRemixServerException } from './utils/errors'; - -export { ErrorBoundary, withErrorBoundary } from '@sentry/react'; -export { withSentry } from './client/performance'; -export { captureRemixErrorBoundaryError } from './client/errors'; -export { browserTracingIntegration } from './client/browserTracingIntegration'; +export * from './server'; +export { captureRemixErrorBoundaryError, withSentry } from './client'; export type { SentryMetaArgs } from './utils/types'; - -/** - * Returns the default Remix integrations. - * - * @param options The options for the SDK. - */ -export function getRemixDefaultIntegrations(options: RemixOptions): Integration[] { - return [ - ...getDefaultNodeIntegrations(options as NodeOptions).filter(integration => integration.name !== 'Http'), - httpIntegration(), - remixIntegration(), - ].filter(int => int) as Integration[]; -} - -/** Initializes Sentry Remix SDK on Node. */ -export function init(options: RemixOptions): NodeClient | undefined { - applySdkMetadata(options, 'remix', ['remix', 'node']); - - if (isInitialized()) { - DEBUG_BUILD && logger.log('SDK already initialized'); - - return; - } - - options.defaultIntegrations = getRemixDefaultIntegrations(options as NodeOptions); - - const client = nodeInit(options as NodeOptions); - - instrumentServer(); - - return client; -} diff --git a/packages/remix/src/index.types.ts b/packages/remix/src/index.types.ts index 5cfb7114bbbc..763a2747f69e 100644 --- a/packages/remix/src/index.types.ts +++ b/packages/remix/src/index.types.ts @@ -12,6 +12,7 @@ import type { RemixOptions } from './utils/remixOptions'; /** Initializes Sentry Remix SDK */ export declare function init(options: RemixOptions): Client | undefined; +export declare const browserTracingIntegration: typeof clientSdk.browserTracingIntegration; export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; diff --git a/packages/remix/src/utils/errors.ts b/packages/remix/src/server/errors.ts similarity index 94% rename from packages/remix/src/utils/errors.ts rename to packages/remix/src/server/errors.ts index 1921ee2a37f2..0a5e9f4918bc 100644 --- a/packages/remix/src/utils/errors.ts +++ b/packages/remix/src/server/errors.ts @@ -14,11 +14,11 @@ import { winterCGRequestToRequestData, } from '@sentry/core'; import type { RequestEventData, Span } from '@sentry/core'; -import { DEBUG_BUILD } from './debug-build'; -import type { RemixOptions } from './remixOptions'; -import { storeFormDataKeys } from './utils'; -import { extractData, isResponse, isRouteErrorResponse } from './vendor/response'; -import type { DataFunction, RemixRequest } from './vendor/types'; +import { DEBUG_BUILD } from '../utils/debug-build'; +import type { RemixOptions } from '../utils/remixOptions'; +import { storeFormDataKeys } from '../utils/utils'; +import { extractData, isResponse, isRouteErrorResponse } from '../utils/vendor/response'; +import type { DataFunction, RemixRequest } from '../utils/vendor/types'; /** * Captures an exception happened in the Remix server. diff --git a/packages/remix/src/server/index.ts b/packages/remix/src/server/index.ts new file mode 100644 index 000000000000..4160a871d165 --- /dev/null +++ b/packages/remix/src/server/index.ts @@ -0,0 +1,122 @@ +// We need to explicitly export @sentry/node as they end up under `default` in ESM builds +// See: https://github.com/getsentry/sentry-javascript/issues/8474 +export { + addBreadcrumb, + addEventProcessor, + addIntegration, + amqplibIntegration, + anrIntegration, + disableAnrDetectionForCallback, + captureCheckIn, + captureConsoleIntegration, + captureEvent, + captureException, + captureFeedback, + captureMessage, + captureSession, + close, + connectIntegration, + consoleIntegration, + contextLinesIntegration, + continueTrace, + createGetModuleFromFilename, + createTransport, + cron, + dedupeIntegration, + defaultStackParser, + endSession, + expressErrorHandler, + expressIntegration, + extraErrorDataIntegration, + fastifyIntegration, + flush, + functionToStringIntegration, + generateInstrumentOnce, + genericPoolIntegration, + getActiveSpan, + getAutoPerformanceIntegrations, + getClient, + getCurrentScope, + getDefaultIntegrations, + getGlobalScope, + getIsolationScope, + getRootSpan, + getSentryRelease, + getSpanDescendants, + getSpanStatusFromHttpCode, + graphqlIntegration, + hapiIntegration, + httpIntegration, + // eslint-disable-next-line deprecation/deprecation + inboundFiltersIntegration, + eventFiltersIntegration, + initOpenTelemetry, + isInitialized, + knexIntegration, + kafkaIntegration, + koaIntegration, + lastEventId, + linkedErrorsIntegration, + localVariablesIntegration, + makeNodeTransport, + modulesIntegration, + mongoIntegration, + mongooseIntegration, + mysql2Integration, + mysqlIntegration, + nativeNodeFetchIntegration, + NodeClient, + nodeContextIntegration, + onUncaughtExceptionIntegration, + onUnhandledRejectionIntegration, + parameterize, + postgresIntegration, + prismaIntegration, + redisIntegration, + requestDataIntegration, + rewriteFramesIntegration, + Scope, + SDK_VERSION, + SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, + SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, + setContext, + setCurrentClient, + setExtra, + setExtras, + setHttpStatus, + setMeasurement, + setTag, + setTags, + setupConnectErrorHandler, + setupExpressErrorHandler, + setupHapiErrorHandler, + setupKoaErrorHandler, + setUser, + spanToBaggageHeader, + spanToJSON, + spanToTraceHeader, + spotlightIntegration, + startInactiveSpan, + startNewTrace, + suppressTracing, + startSession, + startSpan, + startSpanManual, + tediousIntegration, + trpcMiddleware, + updateSpanName, + withActiveSpan, + withIsolationScope, + withMonitor, + withScope, + zodErrorsIntegration, +} from '@sentry/node'; + +// Keeping the `*` exports for backwards compatibility and types +export * from '@sentry/node'; + +export { init, getRemixDefaultIntegrations } from './sdk'; +export { captureRemixServerException } from './errors'; +export { sentryHandleError, wrapHandleErrorWithSentry, instrumentBuild } from './instrumentServer'; diff --git a/packages/remix/src/utils/instrumentServer.ts b/packages/remix/src/server/instrumentServer.ts similarity index 59% rename from packages/remix/src/utils/instrumentServer.ts rename to packages/remix/src/server/instrumentServer.ts index 25878becb82d..f4634805e1e1 100644 --- a/packages/remix/src/utils/instrumentServer.ts +++ b/packages/remix/src/server/instrumentServer.ts @@ -1,19 +1,28 @@ -import type { RequestEventData, WrappedFunction } from '@sentry/core'; +/* eslint-disable max-lines */ +import type { RequestEventData, Span, TransactionSource, WrappedFunction } from '@sentry/core'; import { + SEMANTIC_ATTRIBUTE_SENTRY_OP, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, + SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, continueTrace, fill, + getActiveSpan, getClient, + getRootSpan, getTraceData, hasSpansEnabled, isNodeEnv, loadModule, logger, + setHttpStatus, + spanToJSON, + startSpan, winterCGRequestToRequestData, withIsolationScope, } from '@sentry/core'; -import { DEBUG_BUILD } from './debug-build'; -import { captureRemixServerException, errorHandleDataFunction, errorHandleDocumentRequestFunction } from './errors'; -import { extractData, isDeferredData, isResponse, isRouteErrorResponse, json } from './vendor/response'; +import { DEBUG_BUILD } from '../utils/debug-build'; +import { createRoutes, getTransactionName } from '../utils/utils'; +import { extractData, isDeferredData, isResponse, isRouteErrorResponse, json } from '../utils/vendor/response'; import type { AppData, AppLoadContext, @@ -25,8 +34,10 @@ import type { RemixRequest, RequestHandler, ServerBuild, + ServerRoute, ServerRouteManifest, -} from './vendor/types'; +} from '../utils/vendor/types'; +import { captureRemixServerException, errorHandleDataFunction, errorHandleDocumentRequestFunction } from './errors'; const redirectStatusCodes = new Set([301, 302, 303, 307, 308]); function isRedirectResponse(response: Response): boolean { @@ -77,11 +88,16 @@ export function wrapHandleErrorWithSentry( }; } +function isCloudflareEnv(): boolean { + // eslint-disable-next-line no-restricted-globals + return navigator?.userAgent?.includes('Cloudflare'); +} + function getTraceAndBaggage(): { sentryTrace?: string; sentryBaggage?: string; } { - if (isNodeEnv()) { + if (isNodeEnv() || isCloudflareEnv()) { const traceData = getTraceData(); return { @@ -93,7 +109,7 @@ function getTraceAndBaggage(): { return {}; } -function makeWrappedDocumentRequestFunction() { +function makeWrappedDocumentRequestFunction(instrumentTracing?: boolean) { return function (origDocumentRequestFunction: HandleDocumentRequestFunction): HandleDocumentRequestFunction { return async function ( this: unknown, @@ -103,33 +119,81 @@ function makeWrappedDocumentRequestFunction() { context: EntryContext, loadContext?: Record, ): Promise { - return errorHandleDocumentRequestFunction.call(this, origDocumentRequestFunction, { + const documentRequestContext = { request, responseStatusCode, responseHeaders, context, loadContext, - }); + }; + + if (instrumentTracing) { + const activeSpan = getActiveSpan(); + const rootSpan = activeSpan && getRootSpan(activeSpan); + + const name = rootSpan ? spanToJSON(rootSpan).description : undefined; + + return startSpan( + { + // If we don't have a root span, `onlyIfParent` will lead to the span not being created anyhow + // So we don't need to care too much about the fallback name, it's just for typing purposes.... + name: name || '', + onlyIfParent: true, + attributes: { + method: request.method, + url: request.url, + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.remix', + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'function.remix.document_request', + }, + }, + () => { + return errorHandleDocumentRequestFunction.call(this, origDocumentRequestFunction, documentRequestContext); + }, + ); + } else { + return errorHandleDocumentRequestFunction.call(this, origDocumentRequestFunction, documentRequestContext); + } }; }; } -function makeWrappedDataFunction(origFn: DataFunction, id: string, name: 'action' | 'loader'): DataFunction { +function makeWrappedDataFunction( + origFn: DataFunction, + id: string, + name: 'action' | 'loader', + instrumentTracing?: boolean, +): DataFunction { return async function (this: unknown, args: DataFunctionArgs): Promise { - return errorHandleDataFunction.call(this, origFn, name, args); + if (instrumentTracing) { + return startSpan( + { + op: `function.remix.${name}`, + name: id, + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ui.remix', + name, + }, + }, + (span: Span) => { + return errorHandleDataFunction.call(this, origFn, name, args, span); + }, + ); + } else { + return errorHandleDataFunction.call(this, origFn, name, args); + } }; } const makeWrappedAction = - (id: string) => + (id: string, instrumentTracing?: boolean) => (origAction: DataFunction): DataFunction => { - return makeWrappedDataFunction(origAction, id, 'action'); + return makeWrappedDataFunction(origAction, id, 'action', instrumentTracing); }; const makeWrappedLoader = - (id: string) => + (id: string, instrumentTracing?: boolean) => (origLoader: DataFunction): DataFunction => { - return makeWrappedDataFunction(origLoader, id, 'loader'); + return makeWrappedDataFunction(origLoader, id, 'loader', instrumentTracing); }; function makeWrappedRootLoader() { @@ -176,7 +240,20 @@ function makeWrappedRootLoader() { }; } -function wrapRequestHandler(origRequestHandler: RequestHandler): RequestHandler { +function wrapRequestHandler( + origRequestHandler: RequestHandler, + build: + | ServerBuild + | { build: ServerBuild } + | (() => ServerBuild | { build: ServerBuild } | Promise), + options?: { + instrumentTracing?: boolean; + }, +): RequestHandler { + let resolvedBuild: ServerBuild | { build: ServerBuild }; + let name: string; + let source: TransactionSource; + return async function (this: unknown, request: RemixRequest, loadContext?: AppLoadContext): Promise { const upperCaseMethod = request.method.toUpperCase(); // We don't want to wrap OPTIONS and HEAD requests @@ -184,8 +261,25 @@ function wrapRequestHandler(origRequestHandler: RequestHandler): RequestHandler return origRequestHandler.call(this, request, loadContext); } + let resolvedRoutes: ServerRoute[] | undefined; + + if (options?.instrumentTracing) { + if (typeof build === 'function') { + resolvedBuild = await build(); + } else { + resolvedBuild = build; + } + + // check if the build is nested under `build` key + if ('build' in resolvedBuild) { + resolvedRoutes = createRoutes(resolvedBuild.build.routes); + } else { + resolvedRoutes = createRoutes(resolvedBuild.routes); + } + } + return withIsolationScope(async isolationScope => { - const options = getClient()?.getOptions(); + const clientOptions = getClient()?.getOptions(); let normalizedRequest: RequestEventData = {}; @@ -195,9 +289,16 @@ function wrapRequestHandler(origRequestHandler: RequestHandler): RequestHandler DEBUG_BUILD && logger.warn('Failed to normalize Remix request'); } + if (options?.instrumentTracing && resolvedRoutes) { + const url = new URL(request.url); + [name, source] = getTransactionName(resolvedRoutes, url); + + isolationScope.setTransactionName(name); + } + isolationScope.setSDKProcessingMetadata({ normalizedRequest }); - if (!options || !hasSpansEnabled(options)) { + if (!clientOptions || !hasSpansEnabled(clientOptions)) { return origRequestHandler.call(this, request, loadContext); } @@ -207,6 +308,33 @@ function wrapRequestHandler(origRequestHandler: RequestHandler): RequestHandler baggage: request.headers.get('baggage') || '', }, async () => { + if (options?.instrumentTracing) { + const parentSpan = getActiveSpan(); + const rootSpan = parentSpan && getRootSpan(parentSpan); + rootSpan?.updateName(name); + + return startSpan( + { + name, + attributes: { + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.remix', + [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: source, + [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server', + method: request.method, + }, + }, + async span => { + const res = (await origRequestHandler.call(this, request, loadContext)) as Response; + + if (isResponse(res)) { + setHttpStatus(span, res.status); + } + + return res; + }, + ); + } + return (await origRequestHandler.call(this, request, loadContext)) as Response; }, ); @@ -214,8 +342,14 @@ function wrapRequestHandler(origRequestHandler: RequestHandler): RequestHandler }; } -function instrumentBuildCallback(build: ServerBuild): ServerBuild { - const routes: ServerRouteManifest = {}; +function instrumentBuildCallback( + build: ServerBuild, + options?: { + instrumentTracing?: boolean; + }, +): ServerBuild { + const routes: ServerRouteManifest = build.routes; + const wrappedEntry = { ...build.entry, module: { ...build.entry.module } }; // Not keeping boolean flags like it's done for `requestHandler` functions, @@ -224,7 +358,7 @@ function instrumentBuildCallback(build: ServerBuild): ServerBuild { // We should be able to wrap them, as they may not be wrapped before. const defaultExport = wrappedEntry.module.default as undefined | WrappedFunction; if (defaultExport && !defaultExport.__sentry_original__) { - fill(wrappedEntry.module, 'default', makeWrappedDocumentRequestFunction()); + fill(wrappedEntry.module, 'default', makeWrappedDocumentRequestFunction(options?.instrumentTracing)); } for (const [id, route] of Object.entries(build.routes)) { @@ -232,12 +366,12 @@ function instrumentBuildCallback(build: ServerBuild): ServerBuild { const routeAction = wrappedRoute.module.action as undefined | WrappedFunction; if (routeAction && !routeAction.__sentry_original__) { - fill(wrappedRoute.module, 'action', makeWrappedAction(id)); + fill(wrappedRoute.module, 'action', makeWrappedAction(id, options?.instrumentTracing)); } const routeLoader = wrappedRoute.module.loader as undefined | WrappedFunction; if (routeLoader && !routeLoader.__sentry_original__) { - fill(wrappedRoute.module, 'loader', makeWrappedLoader(id)); + fill(wrappedRoute.module, 'loader', makeWrappedLoader(id, options?.instrumentTracing)); } // Entry module should have a loader function to provide `sentry-trace` and `baggage` @@ -254,7 +388,13 @@ function instrumentBuildCallback(build: ServerBuild): ServerBuild { routes[id] = wrappedRoute; } - return { ...build, routes, entry: wrappedEntry }; + const instrumentedBuild = { ...build, routes }; + + if (wrappedEntry) { + instrumentedBuild.entry = wrappedEntry; + } + + return instrumentedBuild; } /** @@ -262,6 +402,9 @@ function instrumentBuildCallback(build: ServerBuild): ServerBuild { */ export function instrumentBuild( build: ServerBuild | (() => ServerBuild | Promise), + options?: { + instrumentTracing?: boolean; + }, ): ServerBuild | (() => ServerBuild | Promise) { if (typeof build === 'function') { return function () { @@ -269,28 +412,28 @@ export function instrumentBuild( if (resolvedBuild instanceof Promise) { return resolvedBuild.then(build => { - return instrumentBuildCallback(build); + return instrumentBuildCallback(build, options); }); } else { - return instrumentBuildCallback(resolvedBuild); + return instrumentBuildCallback(resolvedBuild, options); } }; } else { - return instrumentBuildCallback(build); + return instrumentBuildCallback(build, options); } } -const makeWrappedCreateRequestHandler = () => +export const makeWrappedCreateRequestHandler = (options?: { instrumentTracing?: boolean }) => function (origCreateRequestHandler: CreateRequestHandlerFunction): CreateRequestHandlerFunction { return function ( this: unknown, build: ServerBuild | (() => Promise), ...args: unknown[] ): RequestHandler { - const newBuild = instrumentBuild(build); + const newBuild = instrumentBuild(build, options); const requestHandler = origCreateRequestHandler.call(this, newBuild, ...args); - return wrapRequestHandler(requestHandler); + return wrapRequestHandler(requestHandler, newBuild, options); }; }; @@ -298,7 +441,7 @@ const makeWrappedCreateRequestHandler = () => * Monkey-patch Remix's `createRequestHandler` from `@remix-run/server-runtime` * which Remix Adapters (https://remix.run/docs/en/v1/api/remix) use underneath. */ -export function instrumentServer(): void { +export function instrumentServer(options?: { instrumentTracing?: boolean }): void { const pkg = loadModule<{ createRequestHandler: CreateRequestHandlerFunction; }>('@remix-run/server-runtime', module); @@ -309,5 +452,5 @@ export function instrumentServer(): void { return; } - fill(pkg, 'createRequestHandler', makeWrappedCreateRequestHandler()); + fill(pkg, 'createRequestHandler', makeWrappedCreateRequestHandler(options)); } diff --git a/packages/remix/src/utils/integrations/http.ts b/packages/remix/src/server/integrations/http.ts similarity index 100% rename from packages/remix/src/utils/integrations/http.ts rename to packages/remix/src/server/integrations/http.ts diff --git a/packages/remix/src/utils/integrations/opentelemetry.ts b/packages/remix/src/server/integrations/opentelemetry.ts similarity index 97% rename from packages/remix/src/utils/integrations/opentelemetry.ts rename to packages/remix/src/server/integrations/opentelemetry.ts index e14d5671bb4d..7ba99421c82f 100644 --- a/packages/remix/src/utils/integrations/opentelemetry.ts +++ b/packages/remix/src/server/integrations/opentelemetry.ts @@ -3,7 +3,7 @@ import { RemixInstrumentation } from 'opentelemetry-instrumentation-remix'; import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, defineIntegration } from '@sentry/core'; import type { Client, IntegrationFn, Span } from '@sentry/core'; import { generateInstrumentOnce, getClient, spanToJSON } from '@sentry/node'; -import type { RemixOptions } from '../remixOptions'; +import type { RemixOptions } from '../../utils/remixOptions'; const INTEGRATION_NAME = 'Remix'; diff --git a/packages/remix/src/server/sdk.ts b/packages/remix/src/server/sdk.ts new file mode 100644 index 000000000000..65a19bebdd28 --- /dev/null +++ b/packages/remix/src/server/sdk.ts @@ -0,0 +1,42 @@ +import { applySdkMetadata, logger } from '@sentry/core'; +import type { Integration } from '@sentry/core'; +import type { NodeClient, NodeOptions } from '@sentry/node'; +import { getDefaultIntegrations as getDefaultNodeIntegrations, init as nodeInit, isInitialized } from '@sentry/node'; + +import { DEBUG_BUILD } from '../utils/debug-build'; +import type { RemixOptions } from '../utils/remixOptions'; +import { instrumentServer } from './instrumentServer'; +import { httpIntegration } from './integrations/http'; +import { remixIntegration } from './integrations/opentelemetry'; + +/** + * Returns the default Remix integrations. + * + * @param options The options for the SDK. + */ +export function getRemixDefaultIntegrations(options: RemixOptions): Integration[] { + return [ + ...getDefaultNodeIntegrations(options as NodeOptions).filter(integration => integration.name !== 'Http'), + httpIntegration(), + remixIntegration(), + ].filter(int => int) as Integration[]; +} + +/** Initializes Sentry Remix SDK on Node. */ +export function init(options: RemixOptions): NodeClient | undefined { + applySdkMetadata(options, 'remix', ['remix', 'node']); + + if (isInitialized()) { + DEBUG_BUILD && logger.log('SDK already initialized'); + + return; + } + + options.defaultIntegrations = getRemixDefaultIntegrations(options as NodeOptions); + + const client = nodeInit(options as NodeOptions); + + instrumentServer(); + + return client; +} diff --git a/packages/remix/src/utils/vendor/types.ts b/packages/remix/src/utils/vendor/types.ts index 19e30b1a78e1..4181a3d77334 100644 --- a/packages/remix/src/utils/vendor/types.ts +++ b/packages/remix/src/utils/vendor/types.ts @@ -1,6 +1,5 @@ import type { Agent } from 'https'; /* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable max-lines */ /* eslint-disable @typescript-eslint/ban-types */ // Types vendored from @remix-run/server-runtime@1.6.0: // https://github.com/remix-run/remix/blob/f3691d51027b93caa3fd2cdfe146d7b62a6eb8f2/packages/remix-server-runtime/server.ts @@ -108,12 +107,7 @@ export type DeferredData = { }; export interface MetaFunction { - (args: { - data: AppData; - parentsData: RouteData; - params: Params; - location: Location; - }): HtmlMetaDescriptor; + (args: { data: AppData; parentsData: RouteData; params: Params; location: Location }): HtmlMetaDescriptor; } export interface HtmlMetaDescriptor { @@ -148,11 +142,7 @@ export interface LoaderFunction { } export interface HeadersFunction { - (args: { - loaderHeaders: Headers; - parentHeaders: Headers; - actionHeaders: Headers; - }): Headers | HeadersInit; + (args: { loaderHeaders: Headers; parentHeaders: Headers; actionHeaders: Headers }): Headers | HeadersInit; } export interface ServerRouteModule extends EntryRouteModule { diff --git a/packages/remix/test/integration/test/client/utils/helpers.ts b/packages/remix/test/integration/test/client/utils/helpers.ts index 56e31cc331c8..97007673703b 100644 --- a/packages/remix/test/integration/test/client/utils/helpers.ts +++ b/packages/remix/test/integration/test/client/utils/helpers.ts @@ -137,10 +137,13 @@ export const countEnvelopes = async ( page.on('request', requestHandler); - setTimeout(() => { - page.off('request', requestHandler); - resolve(reqCount); - }, options?.timeout || 1000); + setTimeout( + () => { + page.off('request', requestHandler); + resolve(reqCount); + }, + options?.timeout || 1000, + ); }); if (options?.url) { diff --git a/packages/replay-canvas/package.json b/packages/replay-canvas/package.json index 860a2f4a0f80..debf55c5fa54 100644 --- a/packages/replay-canvas/package.json +++ b/packages/replay-canvas/package.json @@ -65,7 +65,7 @@ }, "homepage": "https://docs.sentry.io/platforms/javascript/session-replay/", "devDependencies": { - "@sentry-internal/rrweb": "2.31.0" + "@sentry-internal/rrweb": "2.33.0" }, "dependencies": { "@sentry-internal/replay": "9.2.0", diff --git a/packages/replay-internal/package.json b/packages/replay-internal/package.json index 48410e498310..3f19967a5f2a 100644 --- a/packages/replay-internal/package.json +++ b/packages/replay-internal/package.json @@ -48,10 +48,12 @@ "build:tarball": "npm pack", "circularDepCheck": "madge --circular src/index.ts", "clean": "rimraf build sentry-replay-*.tgz", - "fix": "run-s fix:biome fix:eslint", + "fix": "run-s fix:prettier fix:eslint", "fix:eslint": "eslint . --format stylish --fix", - "fix:biome": "biome check --apply .", - "lint": "eslint . --format stylish", + "fix:prettier": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", + "lint": "run-s lint:prettier lint:eslint", + "lint:eslint": "eslint . --format stylish", + "lint:prettier": "prettier --check \"src/**/*.ts\" \"test/**/*.ts\"", "test": "vitest run", "test:watch": "vitest --watch", "yalc:publish": "yalc publish --push --sig" @@ -69,8 +71,8 @@ "devDependencies": { "@babel/core": "^7.17.5", "@sentry-internal/replay-worker": "9.2.0", - "@sentry-internal/rrweb": "2.31.0", - "@sentry-internal/rrweb-snapshot": "2.31.0", + "@sentry-internal/rrweb": "2.33.0", + "@sentry-internal/rrweb-snapshot": "2.33.0", "fflate": "0.8.2", "jest-matcher-utils": "^29.0.0", "jsdom-worker": "^0.2.1" diff --git a/packages/replay-internal/test.setup.ts b/packages/replay-internal/test.setup.ts index c7d1ffa8eb51..55c5bc1704e2 100644 --- a/packages/replay-internal/test.setup.ts +++ b/packages/replay-internal/test.setup.ts @@ -1,5 +1,5 @@ import { printDiffOrStringify } from 'jest-matcher-utils'; -import { vi } from 'vitest'; +import { vi, expect } from 'vitest'; import type { Mocked, MockedFunction } from 'vitest'; /* eslint-disable @typescript-eslint/no-unsafe-member-access */ diff --git a/packages/replay-internal/test/unit/util/logger.test.ts b/packages/replay-internal/test/unit/util/logger.test.ts index 0d349b15ee62..2334ad1d83e6 100644 --- a/packages/replay-internal/test/unit/util/logger.test.ts +++ b/packages/replay-internal/test/unit/util/logger.test.ts @@ -1,4 +1,4 @@ -import { beforeEach, describe, expect, it } from 'vitest'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; import * as SentryCore from '@sentry/core'; import { logger as coreLogger } from '@sentry/core'; diff --git a/packages/solidstart/src/server/index.ts b/packages/solidstart/src/server/index.ts index a6362af1c7f4..948c3c746d0c 100644 --- a/packages/solidstart/src/server/index.ts +++ b/packages/solidstart/src/server/index.ts @@ -50,7 +50,9 @@ export { graphqlIntegration, hapiIntegration, httpIntegration, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, initOpenTelemetry, isInitialized, knexIntegration, diff --git a/packages/solidstart/test/client/errorboundary.test.tsx b/packages/solidstart/test/client/errorboundary.test.tsx index 8ce4e31e89d2..1b7b0c27e69c 100644 --- a/packages/solidstart/test/client/errorboundary.test.tsx +++ b/packages/solidstart/test/client/errorboundary.test.tsx @@ -3,7 +3,7 @@ import type * as SentryBrowser from '@sentry/browser'; import { createTransport, getCurrentScope, setCurrentClient } from '@sentry/core'; import { render } from '@solidjs/testing-library'; import userEvent from '@testing-library/user-event'; -import { vi } from 'vitest'; +import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest'; import { ErrorBoundary } from 'solid-js'; import { BrowserClient, withSentryErrorBoundary } from '../../src/client'; diff --git a/packages/solidstart/test/client/sdk.test.ts b/packages/solidstart/test/client/sdk.test.ts index dc0df6993b81..fccd95eba741 100644 --- a/packages/solidstart/test/client/sdk.test.ts +++ b/packages/solidstart/test/client/sdk.test.ts @@ -1,7 +1,7 @@ import { SDK_VERSION } from '@sentry/solid'; import * as SentrySolid from '@sentry/solid'; -import { vi } from 'vitest'; +import { vi, describe, beforeEach, it, expect } from 'vitest'; import { init as solidStartInit } from '../../src/client'; import { solidRouterBrowserTracingIntegration } from '../../src/client/solidrouter'; diff --git a/packages/solidstart/test/client/solidrouter.test.tsx b/packages/solidstart/test/client/solidrouter.test.tsx index 681b8d7b5ce7..bbb510186938 100644 --- a/packages/solidstart/test/client/solidrouter.test.tsx +++ b/packages/solidstart/test/client/solidrouter.test.tsx @@ -10,7 +10,7 @@ import { import type { MemoryHistory } from '@solidjs/router'; import { MemoryRouter, Navigate, Route, createMemoryHistory } from '@solidjs/router'; import { render } from '@solidjs/testing-library'; -import { vi } from 'vitest'; +import { vi, describe, it, beforeEach, expect } from 'vitest'; import { BrowserClient } from '../../src/client'; import { solidRouterBrowserTracingIntegration, withSentryRouterRouting } from '../../src/client/solidrouter'; diff --git a/packages/solidstart/test/server/errorboundary.test.tsx b/packages/solidstart/test/server/errorboundary.test.tsx index 0d97b6b17a0d..8e03a590db2d 100644 --- a/packages/solidstart/test/server/errorboundary.test.tsx +++ b/packages/solidstart/test/server/errorboundary.test.tsx @@ -3,7 +3,7 @@ import type * as SentryCore from '@sentry/core'; import { createTransport, getCurrentScope, setCurrentClient } from '@sentry/core'; import { render } from '@solidjs/testing-library'; import userEvent from '@testing-library/user-event'; -import { vi } from 'vitest'; +import { vi, describe, beforeEach, it, expect, afterEach } from 'vitest'; import { ErrorBoundary } from 'solid-js'; import { NodeClient, withSentryErrorBoundary } from '../../src/server'; diff --git a/packages/solidstart/test/server/middleware.test.ts b/packages/solidstart/test/server/middleware.test.ts index c1d6ff644b9d..079aa799659d 100644 --- a/packages/solidstart/test/server/middleware.test.ts +++ b/packages/solidstart/test/server/middleware.test.ts @@ -1,5 +1,5 @@ import * as SentryCore from '@sentry/core'; -import { beforeEach, describe, it, vi } from 'vitest'; +import { beforeEach, describe, it, vi, expect } from 'vitest'; import { sentryBeforeResponseMiddleware } from '../../src/server'; import type { ResponseMiddlewareResponse } from '../../src/server'; diff --git a/packages/solidstart/test/server/solidrouter.test.tsx b/packages/solidstart/test/server/solidrouter.test.tsx index 17b3f265a049..c28373bdabbc 100644 --- a/packages/solidstart/test/server/solidrouter.test.tsx +++ b/packages/solidstart/test/server/solidrouter.test.tsx @@ -1,7 +1,7 @@ import type { MemoryHistory } from '@solidjs/router'; import { MemoryRouter, Route, createMemoryHistory } from '@solidjs/router'; import { render } from '@solidjs/testing-library'; -import { vi } from 'vitest'; +import { vi, describe, expect, it } from 'vitest'; import { withSentryRouterRouting as withSentryClientRouterRouting } from '../../src/client/solidrouter'; import { withSentryRouterRouting as withSentryServerRouterRouting } from '../../src/server/solidrouter'; diff --git a/packages/solidstart/test/vite/buildInstrumentation.test.ts b/packages/solidstart/test/vite/buildInstrumentation.test.ts index 52378a668870..f6692479ec74 100644 --- a/packages/solidstart/test/vite/buildInstrumentation.test.ts +++ b/packages/solidstart/test/vite/buildInstrumentation.test.ts @@ -1,5 +1,5 @@ import type { UserConfig } from 'vite'; -import { describe, expect, it, vi } from 'vitest'; +import { describe, expect, it, vi, beforeEach } from 'vitest'; import { makeBuildInstrumentationFilePlugin } from '../../src/vite/buildInstrumentationFile'; const fsAccessMock = vi.fn(); diff --git a/packages/sveltekit/src/server-common/handle.ts b/packages/sveltekit/src/server-common/handle.ts index 48167066c6d7..5f69feb9fbad 100644 --- a/packages/sveltekit/src/server-common/handle.ts +++ b/packages/sveltekit/src/server-common/handle.ts @@ -57,9 +57,9 @@ export const FETCH_PROXY_SCRIPT = ` * * Exported only for testing */ -export function addSentryCodeToPage(options: { injectFetchProxyScript: boolean }): NonNullable< - ResolveOptions['transformPageChunk'] -> { +export function addSentryCodeToPage(options: { + injectFetchProxyScript: boolean; +}): NonNullable { return ({ html }) => { const metaTags = getTraceMetaTags(); const headWithMetaTags = metaTags ? `\n${metaTags}` : ''; diff --git a/packages/sveltekit/src/server-common/utils.ts b/packages/sveltekit/src/server-common/utils.ts index d6f09093b74d..90f47f022e5c 100644 --- a/packages/sveltekit/src/server-common/utils.ts +++ b/packages/sveltekit/src/server-common/utils.ts @@ -19,6 +19,10 @@ export function getTracePropagationData(event: RequestEvent): { sentryTrace: str /** Flush the event queue to ensure that events get sent to Sentry before the response is finished and the lambda ends */ export async function flushIfServerless(): Promise { + if (!process) { + return; + } + const platformSupportsStreaming = !process.env.LAMBDA_TASK_ROOT && !process.env.VERCEL; if (!platformSupportsStreaming) { diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index ccd09570b674..f8844c1e264d 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -52,7 +52,9 @@ export { graphqlIntegration, hapiIntegration, httpIntegration, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, initOpenTelemetry, isInitialized, knexIntegration, diff --git a/packages/sveltekit/src/worker/index.ts b/packages/sveltekit/src/worker/index.ts index a74989b7d28e..8e0e549440ca 100644 --- a/packages/sveltekit/src/worker/index.ts +++ b/packages/sveltekit/src/worker/index.ts @@ -43,6 +43,7 @@ export { getSpanStatusFromHttpCode, getTraceData, getTraceMetaTags, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, isInitialized, lastEventId, diff --git a/packages/sveltekit/test/server/rewriteFramesIntegration.test.ts b/packages/sveltekit/test/server/rewriteFramesIntegration.test.ts index 1d5ca8d4d695..dafae17c318e 100644 --- a/packages/sveltekit/test/server/rewriteFramesIntegration.test.ts +++ b/packages/sveltekit/test/server/rewriteFramesIntegration.test.ts @@ -1,6 +1,7 @@ import { rewriteFramesIntegration } from '@sentry/browser'; import { basename } from '@sentry/core'; import type { Event, StackFrame } from '@sentry/core'; +import { describe, expect, it } from 'vitest'; import { rewriteFramesIteratee } from '../../src/server-common/rewriteFramesIntegration'; import type { GlobalWithSentryValues } from '../../src/vite/injectGlobalValues'; diff --git a/packages/tanstackstart/.eslintrc.js b/packages/tanstackstart/.eslintrc.js new file mode 100644 index 000000000000..54e8382b22a8 --- /dev/null +++ b/packages/tanstackstart/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + env: { + browser: true, + node: true, + }, + parserOptions: { + jsx: true, + }, + extends: ['../../.eslintrc.js'], +}; diff --git a/packages/tanstackstart/LICENSE b/packages/tanstackstart/LICENSE new file mode 100644 index 000000000000..0da96cd2f885 --- /dev/null +++ b/packages/tanstackstart/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Functional Software, Inc. dba Sentry + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/tanstackstart/README.md b/packages/tanstackstart/README.md new file mode 100644 index 000000000000..35e369cf25e9 --- /dev/null +++ b/packages/tanstackstart/README.md @@ -0,0 +1,52 @@ +

+ + Sentry + +

+ +# Official Sentry SDK for TanStack Start (Alpha) + +[![npm version](https://img.shields.io/npm/v/@sentry/tanstackstart.svg)](https://www.npmjs.com/package/@sentry/tanstackstart) +[![npm dm](https://img.shields.io/npm/dm/@sentry/tanstackstart.svg)](https://www.npmjs.com/package/@sentry/tanstackstart) +[![npm dt](https://img.shields.io/npm/dt/@sentry/tanstackstart.svg)](https://www.npmjs.com/package/@sentry/tanstackstart) + +> NOTICE: This package is in alpha state and may be subject to breaking changes. + +## Getting Started + +This SDK does not have docs yet. Stay tuned. + +## Compatibility + +The minimum supported version of TanStack Start is `1.111.12`. + +## Custom Usage + +To set context information or to send manual events, you can use `@sentry/tanstackstart` as follows: + +```ts +import * as Sentry from '@sentry/tanstackstart'; + +// Set user information, as well as tags and further extras +Sentry.setTag('user_mode', 'admin'); +Sentry.setUser({ id: '4711' }); +Sentry.setContext('application_area', { location: 'checkout' }); + +// Add a breadcrumb for future events +Sentry.addBreadcrumb({ + message: '"Add to cart" clicked', + // ... +}); + +// Capture exceptions or messages +Sentry.captureException(new Error('Oh no.')); +Sentry.captureMessage('Hello, world!'); +``` + +## Links + + + +- [Sentry.io](https://sentry.io/?utm_source=github&utm_medium=npm_tanstackstart) +- [Sentry Discord Server](https://discord.gg/Ww9hbqr) +- [Stack Overflow](https://stackoverflow.com/questions/tagged/sentry) diff --git a/packages/tanstackstart/package.json b/packages/tanstackstart/package.json new file mode 100644 index 000000000000..63a7916ff3dd --- /dev/null +++ b/packages/tanstackstart/package.json @@ -0,0 +1,84 @@ +{ + "name": "@sentry/tanstackstart", + "version": "9.2.0", + "description": "Official Sentry SDK for TanStack Start", + "repository": "git://github.com/getsentry/sentry-javascript.git", + "homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/tanstackstart", + "author": "Sentry", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "main": "build/cjs/index.server.js", + "module": "build/esm/index.server.js", + "types": "build/types/index.types.d.ts", + "files": [ + "/build" + ], + "exports": { + "./package.json": "./package.json", + ".": { + "types": "./build/types/index.types.d.ts", + "browser": { + "import": "./build/esm/index.client.js", + "require": "./build/cjs/index.client.js" + }, + "node": "./build/cjs/index.server.js", + "import": "./build/esm/index.server.js" + }, + "./import": { + "import": { + "default": "./build/import-hook.mjs" + } + }, + "./loader": { + "import": { + "default": "./build/loader-hook.mjs" + } + } + }, + "typesVersions": { + "<5.0": { + "build/npm/types/index.d.ts": [ + "build/npm/types-ts3.8/index.d.ts" + ] + } + }, + "publishConfig": { + "access": "public" + }, + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/semantic-conventions": "^1.30.0", + "@sentry-internal/browser-utils": "9.2.0", + "@sentry/core": "9.2.0", + "@sentry/node": "9.2.0", + "@sentry/opentelemetry": "9.2.0", + "@sentry/react": "9.2.0" + }, + "scripts": { + "build": "run-p build:transpile build:types", + "build:dev": "yarn build", + "build:transpile": "rollup -c rollup.npm.config.mjs", + "build:types": "run-s build:types:core build:types:downlevel", + "build:types:core": "tsc -p tsconfig.types.json", + "build:types:downlevel": "yarn downlevel-dts build/types build/types-ts3.8 --to ts3.8", + "build:watch": "run-p build:transpile:watch build:types:watch", + "build:dev:watch": "yarn build:watch", + "build:transpile:watch": "nodemon --ext ts --watch src scripts/buildRollup.ts", + "build:types:watch": "tsc -p tsconfig.types.json --watch", + "build:tarball": "npm pack", + "circularDepCheck": "madge --circular src/index.client.ts && madge --circular src/index.server.ts && madge --circular src/index.types.ts", + "clean": "rimraf build coverage sentry-tanstackstart-*.tgz", + "fix": "eslint . --format stylish --fix", + "lint": "eslint . --format stylish", + "test": "yarn test:unit", + "test:unit": "vitest run", + "test:watch": "vitest --watch", + "yalc:publish": "yalc publish --push --sig" + }, + "volta": { + "extends": "../../package.json" + }, + "sideEffects": false +} diff --git a/packages/tanstackstart/rollup.npm.config.mjs b/packages/tanstackstart/rollup.npm.config.mjs new file mode 100644 index 000000000000..9b334bdbae41 --- /dev/null +++ b/packages/tanstackstart/rollup.npm.config.mjs @@ -0,0 +1,16 @@ +import { makeBaseNPMConfig, makeNPMConfigVariants, makeOtelLoaders } from '@sentry-internal/rollup-utils'; + +export default [ + ...makeNPMConfigVariants( + makeBaseNPMConfig({ + entrypoints: [ + 'src/index.server.ts', + 'src/index.client.ts', + 'src/client/index.ts', + 'src/server/index.ts', + 'src/config/index.ts', + ], + }), + ), + ...makeOtelLoaders('./build', 'sentry-node'), +]; diff --git a/packages/tanstackstart/src/client/index.ts b/packages/tanstackstart/src/client/index.ts new file mode 100644 index 000000000000..c45aad673ad0 --- /dev/null +++ b/packages/tanstackstart/src/client/index.ts @@ -0,0 +1 @@ +export * from '@sentry/react'; diff --git a/packages/tanstackstart/src/config/index.ts b/packages/tanstackstart/src/config/index.ts new file mode 100644 index 000000000000..cb0ff5c3b541 --- /dev/null +++ b/packages/tanstackstart/src/config/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/tanstackstart/src/index.client.ts b/packages/tanstackstart/src/index.client.ts new file mode 100644 index 000000000000..4f1cce44fa36 --- /dev/null +++ b/packages/tanstackstart/src/index.client.ts @@ -0,0 +1 @@ +export * from './client'; diff --git a/packages/tanstackstart/src/index.server.ts b/packages/tanstackstart/src/index.server.ts new file mode 100644 index 000000000000..d08940e2ac5d --- /dev/null +++ b/packages/tanstackstart/src/index.server.ts @@ -0,0 +1,43 @@ +export * from './config'; +export * from './server'; + +/** + * A passthrough error boundary for the server that doesn't depend on any react. Error boundaries don't catch SSR errors + * so they should simply be a passthrough. + */ +export const ErrorBoundary = (props: React.PropsWithChildren): React.ReactNode => { + if (!props.children) { + return null; + } + + if (typeof props.children === 'function') { + return (props.children as () => React.ReactNode)(); + } + + return props.children; +}; + +/** + * A passthrough redux enhancer for the server that doesn't depend on anything from the `@sentry/react` package. + */ +export function createReduxEnhancer() { + return (createStore: unknown) => createStore; +} + +/** + * A passthrough error boundary wrapper for the server that doesn't depend on any react. Error boundaries don't catch + * SSR errors so they should simply be a passthrough. + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function withErrorBoundary

>( + WrappedComponent: React.ComponentType

, +): React.FC

{ + return WrappedComponent as React.FC

; +} + +/** + * Just a passthrough since we're on the server and showing the report dialog on the server doesn't make any sense. + */ +export function showReportDialog(): void { + return; +} diff --git a/packages/tanstackstart/src/index.types.ts b/packages/tanstackstart/src/index.types.ts new file mode 100644 index 000000000000..ede1b27f0b3b --- /dev/null +++ b/packages/tanstackstart/src/index.types.ts @@ -0,0 +1,26 @@ +// We export everything from both the client part of the SDK and from the server part. Some of the exports collide, +// which is not allowed, unless we redefine the colliding exports in this file - which we do below. +export * from './config'; +export * from './client'; +export * from './server'; + +import type { Client, Integration, Options, StackParser } from '@sentry/core'; + +import type * as clientSdk from './client'; +import type * as serverSdk from './server'; + +/** Initializes Sentry TanStack Start SDK */ +export declare function init(options: Options | clientSdk.BrowserOptions | serverSdk.NodeOptions): Client | undefined; + +export declare const linkedErrorsIntegration: typeof clientSdk.linkedErrorsIntegration; +export declare const contextLinesIntegration: typeof clientSdk.contextLinesIntegration; + +export declare const getDefaultIntegrations: (options: Options) => Integration[]; +export declare const defaultStackParser: StackParser; + +export declare function getSentryRelease(fallback?: string): string | undefined; + +export declare const ErrorBoundary: typeof clientSdk.ErrorBoundary; +export declare const createReduxEnhancer: typeof clientSdk.createReduxEnhancer; +export declare const showReportDialog: typeof clientSdk.showReportDialog; +export declare const withErrorBoundary: typeof clientSdk.withErrorBoundary; diff --git a/packages/tanstackstart/src/server/index.ts b/packages/tanstackstart/src/server/index.ts new file mode 100644 index 000000000000..d61c75b7bfb4 --- /dev/null +++ b/packages/tanstackstart/src/server/index.ts @@ -0,0 +1 @@ +export * from '@sentry/node'; diff --git a/packages/tanstackstart/test/temp.test.ts b/packages/tanstackstart/test/temp.test.ts new file mode 100644 index 000000000000..28874b0d1c84 --- /dev/null +++ b/packages/tanstackstart/test/temp.test.ts @@ -0,0 +1,7 @@ +import { describe, it, expect } from 'vitest'; + +describe('Basic test suite', () => { + it('should pass', () => { + expect(true).toBe(true); + }); +}); diff --git a/packages/tanstackstart/test/tsconfig.json b/packages/tanstackstart/test/tsconfig.json new file mode 100644 index 000000000000..38ca0b13bcdd --- /dev/null +++ b/packages/tanstackstart/test/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../tsconfig.test.json" +} diff --git a/packages/tanstackstart/tsconfig.json b/packages/tanstackstart/tsconfig.json new file mode 100644 index 000000000000..20cf507e5203 --- /dev/null +++ b/packages/tanstackstart/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*"], + "compilerOptions": { + "lib": ["es2018", "es2020.string"], + "module": "Node16" + } +} diff --git a/packages/tanstackstart/tsconfig.test.json b/packages/tanstackstart/tsconfig.test.json new file mode 100644 index 000000000000..bbbebba51d18 --- /dev/null +++ b/packages/tanstackstart/tsconfig.test.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "include": ["test/**/*", "vite.config.ts"], + "compilerOptions": { + "types": ["node"], + "lib": ["DOM", "ESNext"] + } +} diff --git a/packages/tanstackstart/tsconfig.types.json b/packages/tanstackstart/tsconfig.types.json new file mode 100644 index 000000000000..b1a51db073c2 --- /dev/null +++ b/packages/tanstackstart/tsconfig.types.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "outDir": "build/types" + } +} diff --git a/packages/tanstackstart/vite.config.ts b/packages/tanstackstart/vite.config.ts new file mode 100644 index 000000000000..f18ec92095bc --- /dev/null +++ b/packages/tanstackstart/vite.config.ts @@ -0,0 +1,8 @@ +import baseConfig from '../../vite/vite.config'; + +export default { + ...baseConfig, + test: { + ...baseConfig.test, + }, +}; diff --git a/packages/vercel-edge/src/index.ts b/packages/vercel-edge/src/index.ts index ab03497ea0c6..eb6429c441fa 100644 --- a/packages/vercel-edge/src/index.ts +++ b/packages/vercel-edge/src/index.ts @@ -65,7 +65,9 @@ export { getSpanDescendants, continueTrace, functionToStringIntegration, + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration, + eventFiltersIntegration, linkedErrorsIntegration, requestDataIntegration, extraErrorDataIntegration, @@ -85,9 +87,6 @@ export { } from '@sentry/core'; export { VercelEdgeClient } from './client'; -export { - getDefaultIntegrations, - init, -} from './sdk'; +export { getDefaultIntegrations, init } from './sdk'; export { winterCGFetchIntegration } from './integrations/wintercg-fetch'; diff --git a/packages/vercel-edge/src/sdk.ts b/packages/vercel-edge/src/sdk.ts index a816b02e27a9..efb3a8dfcd0c 100644 --- a/packages/vercel-edge/src/sdk.ts +++ b/packages/vercel-edge/src/sdk.ts @@ -51,6 +51,8 @@ const nodeStackParser = createStackParser(nodeStackLineParser()); export function getDefaultIntegrations(options: Options): Integration[] { return [ dedupeIntegration(), + // TODO(v10): Replace with `eventFiltersIntegration` once we remove the deprecated `inboundFiltersIntegration` + // eslint-disable-next-line deprecation/deprecation inboundFiltersIntegration(), functionToStringIntegration(), linkedErrorsIntegration(), diff --git a/vite/vite.config.ts b/vite/vite.config.ts index 2717ad778e1d..a26454ad5712 100644 --- a/vite/vite.config.ts +++ b/vite/vite.config.ts @@ -5,7 +5,6 @@ export default defineConfig({ __DEBUG_BUILD__: true, }, test: { - globals: true, coverage: { enabled: true, reportsDirectory: './coverage', diff --git a/yarn.lock b/yarn.lock index 0c7b60c816e1..d58829b21c3a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1287,6 +1287,11 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.0.tgz#f02ba6d34e88fadd5e8861e8b38902f43cc1c819" integrity sha512-qETICbZSLe7uXv9VE8T/RWOdIE5qqyTucOt4zLYMafj2MRO271VGgLd4RACJMeBO37UPWhXiKMBk7YlJ0fOzQA== +"@babel/compat-data@^7.26.5": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" + integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== + "@babel/core@7.18.10": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" @@ -1329,6 +1334,27 @@ json5 "^2.2.3" semver "^6.3.1" +"@babel/core@^7.21.8": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.9.tgz#71838542a4b1e49dfed353d7acbc6eb89f4a76f2" + integrity sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw== + dependencies: + "@ampproject/remapping" "^2.2.0" + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/helper-compilation-targets" "^7.26.5" + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helpers" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/traverse" "^7.26.9" + "@babel/types" "^7.26.9" + convert-source-map "^2.0.0" + debug "^4.1.0" + gensync "^1.0.0-beta.2" + json5 "^2.2.3" + semver "^6.3.1" + "@babel/generator@7.18.12": version "7.18.12" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4" @@ -1349,6 +1375,17 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" +"@babel/generator@^7.21.5", "@babel/generator@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.9.tgz#75a9482ad3d0cc7188a537aa4910bc59db67cbca" + integrity sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg== + dependencies: + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -1363,6 +1400,13 @@ dependencies: "@babel/types" "^7.24.7" +"@babel/helper-annotate-as-pure@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" + integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== + dependencies: + "@babel/types" "^7.25.9" + "@babel/helper-builder-binary-assignment-operator-visitor@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.15.tgz#5426b109cf3ad47b91120f8328d8ab1be8b0b956" @@ -1381,6 +1425,17 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-compilation-targets@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" + integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== + dependencies: + "@babel/compat-data" "^7.26.5" + "@babel/helper-validator-option" "^7.25.9" + browserslist "^4.24.0" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.21.0", "@babel/helper-create-class-features-plugin@^7.24.1", "@babel/helper-create-class-features-plugin@^7.24.4", "@babel/helper-create-class-features-plugin@^7.24.7", "@babel/helper-create-class-features-plugin@^7.25.0", "@babel/helper-create-class-features-plugin@^7.5.5": version "7.25.4" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz#57eaf1af38be4224a9d9dd01ddde05b741f50e14" @@ -1394,6 +1449,19 @@ "@babel/traverse" "^7.25.4" semver "^6.3.1" +"@babel/helper-create-class-features-plugin@^7.25.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.26.9.tgz#d6f83e3039547fbb39967e78043cd3c8b7820c71" + integrity sha512-ubbUqCofvxPRurw5L8WTsCLSkQiVpov4Qx0WMA+jUN+nXBK8ADPlJO1grkFw5CWKC5+sZSOfuGMdX1aI1iT9Sg== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/helper-replace-supers" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/traverse" "^7.26.9" + semver "^6.3.1" + "@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.15", "@babel/helper-create-regexp-features-plugin@^7.22.5": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.15.tgz#5ee90093914ea09639b01c711db0d6775e558be1" @@ -1456,6 +1524,14 @@ "@babel/traverse" "^7.24.8" "@babel/types" "^7.24.8" +"@babel/helper-member-expression-to-functions@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" + integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-module-imports@7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" @@ -1494,11 +1570,23 @@ dependencies: "@babel/types" "^7.24.7" +"@babel/helper-optimise-call-expression@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" + integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== + dependencies: + "@babel/types" "^7.25.9" + "@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.16.7", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.18.9", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.24.7", "@babel/helper-plugin-utils@^7.24.8", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz#9cbdd63a9443a2c92a725cca7ebca12cc8dd9f46" integrity sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw== +"@babel/helper-plugin-utils@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" + integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== + "@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9", "@babel/helper-remap-async-to-generator@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.20.tgz#7b68e1cb4fa964d2996fd063723fb48eca8498e0" @@ -1517,6 +1605,15 @@ "@babel/helper-optimise-call-expression" "^7.24.7" "@babel/traverse" "^7.25.0" +"@babel/helper-replace-supers@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.26.5.tgz#6cb04e82ae291dae8e72335dfe438b0725f14c8d" + integrity sha512-bJ6iIVdYX1YooY2X7w1q6VITt+LnUILtNk7zT78ykuwStx8BauCzxvFqFaHjOpW1bVnSUM1PN1f0p5P21wHxvg== + dependencies: + "@babel/helper-member-expression-to-functions" "^7.25.9" + "@babel/helper-optimise-call-expression" "^7.25.9" + "@babel/traverse" "^7.26.5" + "@babel/helper-simple-access@^7.22.5": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz#bcade8da3aec8ed16b9c4953b74e506b51b5edb3" @@ -1533,6 +1630,14 @@ "@babel/traverse" "^7.24.7" "@babel/types" "^7.24.7" +"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" + integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== + dependencies: + "@babel/traverse" "^7.25.9" + "@babel/types" "^7.25.9" + "@babel/helper-split-export-declaration@^7.22.6": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz#83949436890e07fa3d6873c61a96e3bbf692d856" @@ -1572,6 +1677,14 @@ "@babel/template" "^7.25.9" "@babel/types" "^7.26.0" +"@babel/helpers@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.26.9.tgz#28f3fb45252fc88ef2dc547c8a911c255fc9fef6" + integrity sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA== + dependencies: + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" + "@babel/highlight@^7.10.4": version "7.24.7" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.7.tgz#a05ab1df134b286558aae0ed41e6c5f731bf409d" @@ -1589,6 +1702,13 @@ dependencies: "@babel/types" "^7.26.3" +"@babel/parser@^7.23.6", "@babel/parser@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" + integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== + dependencies: + "@babel/types" "^7.26.9" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.24.4": version "7.24.4" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.4.tgz#6125f0158543fb4edf1c22f322f3db67f21cb3e1" @@ -1802,7 +1922,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.14.5" -"@babel/plugin-syntax-decorators@^7.16.7", "@babel/plugin-syntax-decorators@^7.23.3", "@babel/plugin-syntax-decorators@^7.24.7": +"@babel/plugin-syntax-decorators@^7.16.7", "@babel/plugin-syntax-decorators@^7.22.10", "@babel/plugin-syntax-decorators@^7.23.3", "@babel/plugin-syntax-decorators@^7.24.7": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.25.9.tgz#986b4ca8b7b5df3f67cee889cedeffc2e2bf14b3" integrity sha512-ryzI0McXUPJnRCvMo4lumIKZUzhYUO/ScI+Mz4YVaTLt04DHNSjEUjKVvbzQjZFLuod/cYEc07mJWhzl6v4DPg== @@ -1858,6 +1978,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" +"@babel/plugin-syntax-jsx@^7.21.4", "@babel/plugin-syntax-jsx@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz#a34313a178ea56f1951599b929c1ceacee719290" + integrity sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" @@ -1921,6 +2048,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.24.7" +"@babel/plugin-syntax-typescript@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.9.tgz#67dda2b74da43727cf21d46cf9afef23f4365399" + integrity sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-syntax-unicode-sets-regex@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" @@ -2127,6 +2261,14 @@ "@babel/helper-plugin-utils" "^7.24.0" "@babel/helper-simple-access" "^7.22.5" +"@babel/plugin-transform-modules-commonjs@^7.25.9": + version "7.26.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" + integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== + dependencies: + "@babel/helper-module-transforms" "^7.26.0" + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/plugin-transform-modules-systemjs@^7.18.9", "@babel/plugin-transform-modules-systemjs@^7.24.1": version "7.24.1" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.1.tgz#2b9625a3d4e445babac9788daec39094e6b11e3e" @@ -2328,6 +2470,17 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.24.7" "@babel/plugin-syntax-typescript" "^7.24.7" +"@babel/plugin-transform-typescript@^7.25.9": + version "7.26.8" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.26.8.tgz#2e9caa870aa102f50d7125240d9dbf91334b0950" + integrity sha512-bME5J9AC8ChwA7aEPJ6zym3w7aObZULHhbNLU0bKUhKsAkylkzUdq+0kdymh9rzi8nlNFl2bmldFBCKNJBUpuw== + dependencies: + "@babel/helper-annotate-as-pure" "^7.25.9" + "@babel/helper-create-class-features-plugin" "^7.25.9" + "@babel/helper-plugin-utils" "^7.26.5" + "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" + "@babel/plugin-syntax-typescript" "^7.25.9" + "@babel/plugin-transform-typescript@~7.4.0": version "7.4.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.4.5.tgz#ab3351ba35307b79981993536c93ff8be050ba28" @@ -2581,6 +2734,17 @@ "@babel/helper-validator-option" "^7.16.7" "@babel/plugin-transform-typescript" "^7.16.7" +"@babel/preset-typescript@^7.21.5": + version "7.26.0" + resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.26.0.tgz#4a570f1b8d104a242d923957ffa1eaff142a106d" + integrity sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg== + dependencies: + "@babel/helper-plugin-utils" "^7.25.9" + "@babel/helper-validator-option" "^7.25.9" + "@babel/plugin-syntax-jsx" "^7.25.9" + "@babel/plugin-transform-modules-commonjs" "^7.25.9" + "@babel/plugin-transform-typescript" "^7.25.9" + "@babel/regjsgen@^0.8.0": version "0.8.0" resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" @@ -2630,6 +2794,15 @@ "@babel/parser" "^7.25.9" "@babel/types" "^7.25.9" +"@babel/template@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.26.9.tgz#4577ad3ddf43d194528cff4e1fa6b232fa609bb2" + integrity sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/parser" "^7.26.9" + "@babel/types" "^7.26.9" + "@babel/traverse@^7.18.10", "@babel/traverse@^7.22.10", "@babel/traverse@^7.23.9", "@babel/traverse@^7.24.7", "@babel/traverse@^7.24.8", "@babel/traverse@^7.25.0", "@babel/traverse@^7.25.4", "@babel/traverse@^7.25.9", "@babel/traverse@^7.4.5", "@babel/traverse@^7.7.0", "@babel/traverse@^7.7.2": version "7.26.4" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.4.tgz#ac3a2a84b908dde6d463c3bfa2c5fdc1653574bd" @@ -2643,6 +2816,19 @@ debug "^4.3.1" globals "^11.1.0" +"@babel/traverse@^7.23.2", "@babel/traverse@^7.23.7", "@babel/traverse@^7.26.5", "@babel/traverse@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.9.tgz#4398f2394ba66d05d988b2ad13c219a2c857461a" + integrity sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.9" + "@babel/parser" "^7.26.9" + "@babel/template" "^7.26.9" + "@babel/types" "^7.26.9" + debug "^4.3.1" + globals "^11.1.0" + "@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.20.7", "@babel/types@^7.21.5", "@babel/types@^7.22.10", "@babel/types@^7.22.15", "@babel/types@^7.22.17", "@babel/types@^7.22.19", "@babel/types@^7.23.6", "@babel/types@^7.23.9", "@babel/types@^7.24.7", "@babel/types@^7.24.8", "@babel/types@^7.25.4", "@babel/types@^7.25.6", "@babel/types@^7.25.9", "@babel/types@^7.26.0", "@babel/types@^7.26.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.7.2": version "7.26.3" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.3.tgz#37e79830f04c2b5687acc77db97fbc75fb81f3c0" @@ -2651,65 +2837,19 @@ "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" +"@babel/types@^7.22.5", "@babel/types@^7.26.9": + version "7.26.9" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" + integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@biomejs/biome@^1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.5.2.tgz#fdc194125a904ec69a87cb48b03141b6c070df66" - integrity sha512-LhycxGQBQLmfv6M3e4tMfn/XKcUWyduDYOlCEBrHXJ2mMth2qzYt1JWypkWp+XmU/7Hl2dKvrP4mZ5W44+nWZw== - optionalDependencies: - "@biomejs/cli-darwin-arm64" "1.5.2" - "@biomejs/cli-darwin-x64" "1.5.2" - "@biomejs/cli-linux-arm64" "1.5.2" - "@biomejs/cli-linux-arm64-musl" "1.5.2" - "@biomejs/cli-linux-x64" "1.5.2" - "@biomejs/cli-linux-x64-musl" "1.5.2" - "@biomejs/cli-win32-arm64" "1.5.2" - "@biomejs/cli-win32-x64" "1.5.2" - -"@biomejs/cli-darwin-arm64@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.5.2.tgz#fc744f6ac454ce18b1b36d306c77b2bdb216d6ae" - integrity sha512-3JVl08aHKsPyf0XL9SEj1lssIMmzOMAn2t1zwZKBiy/mcZdb0vuyMSTM5haMQ/90wEmrkYN7zux777PHEGrGiw== - -"@biomejs/cli-darwin-x64@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.5.2.tgz#2439a338166b9acb6d56939bd9e8e6331ee43dcb" - integrity sha512-QAPW9rZb/AgucUx+ogMg+9eJNipQDqvabktC5Tx4Aqb/mFzS6eDqNP7O0SbGz3DtC5Y2LATEj6o6zKIQ4ZT+3w== - -"@biomejs/cli-linux-arm64-musl@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.5.2.tgz#fe5cafb9ff34ebfed7a5abe28a71cfbddd4de70f" - integrity sha512-Z29SjaOyO4QfajplNXSjLx17S79oPN42D094zjE24z7C7p3NxvLhKLygtSP9emgaXkcoESe2chOzF4IrGy/rlg== - -"@biomejs/cli-linux-arm64@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.5.2.tgz#2fd9305441d9df0aca5dfa9e56004d951aea0ec9" - integrity sha512-fVLrUgIlo05rO4cNu+Py5EwwmXnXhWH+8KrNlWkr2weMYjq85SihUsuWWKpmqU+bUVR+m5gwfcIXZVWYVCJMHw== - -"@biomejs/cli-linux-x64-musl@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.5.2.tgz#80cc7eb91ea10aca0a17e6296fa468b0b3332793" - integrity sha512-ZolquPEjWYUmGeERS8svHOOT7OXEeoriPnV8qptgWJmYF9EO9HUGRn1UtCvdVziDYK+u1A7PxjOdkY1B00ty5A== - -"@biomejs/cli-linux-x64@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.5.2.tgz#9247165d0514a6f0fa17f9c8cd49c7d9769a9641" - integrity sha512-ixqJtUHtF0ho1+1DTZQLAEwHGSqvmvHhAAFXZQoaSdABn+IcITYExlFVA3bGvASy/xtPjRhTx42hVwPtLwMHwg== - -"@biomejs/cli-win32-arm64@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.5.2.tgz#86d709f835547537f87fd07c35b6ef3b97dca54f" - integrity sha512-DN4cXSAoFTdjOoh7f+JITj1uQgQSXt+1pVea9bFrpbgip+ZwkONqQq+jUcmFMMehbp9LuiVtNXFz/ReHn6FY7A== - -"@biomejs/cli-win32-x64@1.5.2": - version "1.5.2" - resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.5.2.tgz#607f0e4c01c22e573785bd69be2d7be3415838f8" - integrity sha512-YvWWXZmk936FdrXqc2jcP6rfsXsNBIs9MKBQQoVXIihwNNRiAaBD9Iwa/ouU1b7Zxq2zETgeuRewVJickFuVOw== - "@cloudflare/kv-asset-handler@0.3.4", "@cloudflare/kv-asset-handler@^0.3.4": version "0.3.4" resolved "https://registry.yarnpkg.com/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz#5cc152847c8ae4d280ec5d7f4f6ba8c976b585c3" @@ -3035,6 +3175,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz#51299374de171dbd80bb7d838e1cfce9af36f353" integrity sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ== +"@esbuild/aix-ppc64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz#38848d3e25afe842a7943643cbcd387cc6e13461" + integrity sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA== + "@esbuild/android-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz#bafb75234a5d3d1b690e7c2956a599345e84a2fd" @@ -3070,6 +3215,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz#58565291a1fe548638adb9c584237449e5e14018" integrity sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw== +"@esbuild/android-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz#f592957ae8b5643129fa889c79e69cd8669bb894" + integrity sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg== + "@esbuild/android-arm@0.15.18": version "0.15.18" resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.18.tgz#266d40b8fdcf87962df8af05b76219bc786b4f80" @@ -3110,6 +3260,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.23.1.tgz#5eb8c652d4c82a2421e3395b808e6d9c42c862ee" integrity sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ== +"@esbuild/android-arm@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.24.2.tgz#72d8a2063aa630308af486a7e5cbcd1e134335b3" + integrity sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q== + "@esbuild/android-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.19.tgz#658368ef92067866d95fb268719f98f363d13ae1" @@ -3145,6 +3300,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.23.1.tgz#ae19d665d2f06f0f48a6ac9a224b3f672e65d517" integrity sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg== +"@esbuild/android-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.24.2.tgz#9a7713504d5f04792f33be9c197a882b2d88febb" + integrity sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw== + "@esbuild/darwin-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz#584c34c5991b95d4d48d333300b1a4e2ff7be276" @@ -3180,6 +3340,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz#05b17f91a87e557b468a9c75e9d85ab10c121b16" integrity sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q== +"@esbuild/darwin-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz#02ae04ad8ebffd6e2ea096181b3366816b2b5936" + integrity sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA== + "@esbuild/darwin-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz#7751d236dfe6ce136cce343dce69f52d76b7f6cb" @@ -3215,6 +3380,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz#c58353b982f4e04f0d022284b8ba2733f5ff0931" integrity sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw== +"@esbuild/darwin-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz#9ec312bc29c60e1b6cecadc82bd504d8adaa19e9" + integrity sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA== + "@esbuild/freebsd-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz#cacd171665dd1d500f45c167d50c6b7e539d5fd2" @@ -3250,6 +3420,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz#f9220dc65f80f03635e1ef96cfad5da1f446f3bc" integrity sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA== +"@esbuild/freebsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz#5e82f44cb4906d6aebf24497d6a068cfc152fa00" + integrity sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg== + "@esbuild/freebsd-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz#0769456eee2a08b8d925d7c00b79e861cb3162e4" @@ -3285,6 +3460,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz#69bd8511fa013b59f0226d1609ac43f7ce489730" integrity sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g== +"@esbuild/freebsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz#3fb1ce92f276168b75074b4e51aa0d8141ecce7f" + integrity sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q== + "@esbuild/linux-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz#38e162ecb723862c6be1c27d6389f48960b68edb" @@ -3320,6 +3500,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz#8050af6d51ddb388c75653ef9871f5ccd8f12383" integrity sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g== +"@esbuild/linux-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz#856b632d79eb80aec0864381efd29de8fd0b1f43" + integrity sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg== + "@esbuild/linux-arm@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz#1a2cd399c50040184a805174a6d89097d9d1559a" @@ -3355,6 +3540,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz#ecaabd1c23b701070484990db9a82f382f99e771" integrity sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ== +"@esbuild/linux-arm@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz#c846b4694dc5a75d1444f52257ccc5659021b736" + integrity sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA== + "@esbuild/linux-ia32@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz#e28c25266b036ce1cabca3c30155222841dc035a" @@ -3390,6 +3580,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz#3ed2273214178109741c09bd0687098a0243b333" integrity sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ== +"@esbuild/linux-ia32@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz#f8a16615a78826ccbb6566fab9a9606cfd4a37d5" + integrity sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw== + "@esbuild/linux-loong64@0.15.18": version "0.15.18" resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.18.tgz#128b76ecb9be48b60cf5cfc1c63a4f00691a3239" @@ -3435,6 +3630,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz#a0fdf440b5485c81b0fbb316b08933d217f5d3ac" integrity sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw== +"@esbuild/linux-loong64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz#1c451538c765bf14913512c76ed8a351e18b09fc" + integrity sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ== + "@esbuild/linux-mips64el@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz#f5d2a0b8047ea9a5d9f592a178ea054053a70289" @@ -3470,6 +3670,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz#e11a2806346db8375b18f5e104c5a9d4e81807f6" integrity sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q== +"@esbuild/linux-mips64el@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz#0846edeefbc3d8d50645c51869cc64401d9239cb" + integrity sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw== + "@esbuild/linux-ppc64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz#876590e3acbd9fa7f57a2c7d86f83717dbbac8c7" @@ -3505,6 +3710,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz#06a2744c5eaf562b1a90937855b4d6cf7c75ec96" integrity sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw== +"@esbuild/linux-ppc64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz#8e3fc54505671d193337a36dfd4c1a23b8a41412" + integrity sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw== + "@esbuild/linux-riscv64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz#7f49373df463cd9f41dc34f9b2262d771688bf09" @@ -3540,6 +3750,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz#65b46a2892fc0d1af4ba342af3fe0fa4a8fe08e7" integrity sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA== +"@esbuild/linux-riscv64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz#6a1e92096d5e68f7bb10a0d64bb5b6d1daf9a694" + integrity sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q== + "@esbuild/linux-s390x@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz#e2afd1afcaf63afe2c7d9ceacd28ec57c77f8829" @@ -3575,6 +3790,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz#e71ea18c70c3f604e241d16e4e5ab193a9785d6f" integrity sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw== +"@esbuild/linux-s390x@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz#ab18e56e66f7a3c49cb97d337cd0a6fea28a8577" + integrity sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw== + "@esbuild/linux-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz#8a0e9738b1635f0c53389e515ae83826dec22aa4" @@ -3610,6 +3830,16 @@ resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz#d47f97391e80690d4dfe811a2e7d6927ad9eed24" integrity sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ== +"@esbuild/linux-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz#8140c9b40da634d380b0b29c837a0b4267aff38f" + integrity sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q== + +"@esbuild/netbsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz#65f19161432bafb3981f5f20a7ff45abb2e708e6" + integrity sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw== + "@esbuild/netbsd-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz#c29fb2453c6b7ddef9a35e2c18b37bda1ae5c462" @@ -3645,11 +3875,21 @@ resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz#44e743c9778d57a8ace4b72f3c6b839a3b74a653" integrity sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA== +"@esbuild/netbsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz#7a3a97d77abfd11765a72f1c6f9b18f5396bcc40" + integrity sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw== + "@esbuild/openbsd-arm64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz#05c5a1faf67b9881834758c69f3e51b7dee015d7" integrity sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q== +"@esbuild/openbsd-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz#58b00238dd8f123bfff68d3acc53a6ee369af89f" + integrity sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A== + "@esbuild/openbsd-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz#95e75a391403cb10297280d524d66ce04c920691" @@ -3685,6 +3925,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz#2e58ae511bacf67d19f9f2dcd9e8c5a93f00c273" integrity sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA== +"@esbuild/openbsd-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz#0ac843fda0feb85a93e288842936c21a00a8a205" + integrity sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA== + "@esbuild/sunos-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz#722eaf057b83c2575937d3ffe5aeb16540da7273" @@ -3720,6 +3965,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz#adb022b959d18d3389ac70769cef5a03d3abd403" integrity sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA== +"@esbuild/sunos-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz#8b7aa895e07828d36c422a4404cc2ecf27fb15c6" + integrity sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig== + "@esbuild/win32-arm64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz#9aa9dc074399288bdcdd283443e9aeb6b9552b6f" @@ -3755,6 +4005,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz#84906f50c212b72ec360f48461d43202f4c8b9a2" integrity sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A== +"@esbuild/win32-arm64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz#c023afb647cabf0c3ed13f0eddfc4f1d61c66a85" + integrity sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ== + "@esbuild/win32-ia32@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz#95ad43c62ad62485e210f6299c7b2571e48d2b03" @@ -3790,6 +4045,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz#5e3eacc515820ff729e90d0cb463183128e82fac" integrity sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ== +"@esbuild/win32-ia32@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz#96c356132d2dda990098c8b8b951209c3cd743c2" + integrity sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA== + "@esbuild/win32-x64@0.17.19": version "0.17.19" resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz#8cfaf2ff603e9aabb910e9c0558c26cf32744061" @@ -3825,6 +4085,11 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699" integrity sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg== +"@esbuild/win32-x64@0.24.2": + version "0.24.2" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz#34aa0b52d0fbb1a654b596acfa595f0c7b77a77b" + integrity sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg== + "@eslint-community/eslint-utils@^4.1.2", "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" @@ -4963,7 +5228,7 @@ semver "^7.3.5" which "^2.0.2" -"@npmcli/git@^4.0.0": +"@npmcli/git@^4.0.0", "@npmcli/git@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@npmcli/git/-/git-4.1.0.tgz#ab0ad3fd82bc4d8c1351b6c62f0fa56e8fe6afa6" integrity sha512-9hwoB3gStVfa0N31ymBmrX+GuDGdVA/QWShZVqE0HK2Af+7QGGrCTbZia/SW0ImUTjTne7SP91qxDmtXvDHRPQ== @@ -5011,6 +5276,19 @@ resolved "https://registry.yarnpkg.com/@npmcli/node-gyp/-/node-gyp-3.0.0.tgz#101b2d0490ef1aa20ed460e4c0813f0db560545a" integrity sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA== +"@npmcli/package-json@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/package-json/-/package-json-4.0.1.tgz#1a07bf0e086b640500791f6bf245ff43cc27fa37" + integrity sha512-lRCEGdHZomFsURroh522YvA/2cVb9oPIJrjHanCJZkiasz1BzcnLr3tBJhlV7S86MBJBuAQ33is2D60YitZL2Q== + dependencies: + "@npmcli/git" "^4.1.0" + glob "^10.2.2" + hosted-git-info "^6.1.1" + json-parse-even-better-errors "^3.0.0" + normalize-package-data "^5.0.0" + proc-log "^3.0.0" + semver "^7.5.3" + "@npmcli/promise-spawn@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz#53283b5f18f855c6925f23c24e67c911501ef573" @@ -5565,17 +5843,10 @@ dependencies: "@opentelemetry/api" "^1.0.0" -"@opentelemetry/api-logs@0.56.0": - version "0.56.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.56.0.tgz#68f8c51ca905c260b610c8a3c67d3f9fa3d59a45" - integrity sha512-Wr39+94UNNG3Ei9nv3pHd4AJ63gq5nSemMRpCd8fPwDL9rN3vK26lzxfH27mw16XzOSO+TpyQwBAMaLxaPWG0g== - dependencies: - "@opentelemetry/api" "^1.3.0" - -"@opentelemetry/api-logs@0.57.1": - version "0.57.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.57.1.tgz#97ebd714f0b1fcdf896e85c465ae5c5b22747425" - integrity sha512-I4PHczeujhQAQv6ZBzqHYEUiggZL4IdSMixtVD3EYqbdrjujE7kRfI5QohjlPoJm8BvenoW5YaTMWRrbpot6tg== +"@opentelemetry/api-logs@0.57.2": + version "0.57.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.57.2.tgz#d4001b9aa3580367b40fe889f3540014f766cc87" + integrity sha512-uIX52NnTM0iBh84MShlpouI7UKqkZ7MrUszTmaypHBu4r7NofznSnQRfJ+uUeDtQDj6w8eFGg5KBLDAwAPz1+A== dependencies: "@opentelemetry/api" "^1.3.0" @@ -5617,251 +5888,239 @@ "@opentelemetry/context-base" "^0.12.0" semver "^7.1.3" -"@opentelemetry/instrumentation-amqplib@^0.46.0": - version "0.46.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.0.tgz#8b0c61213034780a79c16216c62eee0ce1457ddf" - integrity sha512-04VHHV1KIN/c1wLWwzmLI02d/welgscBJ4BuDqrHaxd+ZIdlVXK9UYQsYf3JwSeF52z/4YoSzr8bfdVBSWoMAg== +"@opentelemetry/instrumentation-amqplib@^0.46.1": + version "0.46.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-amqplib/-/instrumentation-amqplib-0.46.1.tgz#7101678488d0e942162ca85c9ac6e93e1f3e0008" + integrity sha512-AyXVnlCf/xV3K/rNumzKxZqsULyITJH6OVLiW6730JPRqWA7Zc9bvYoVNpN6iOpTU8CasH34SU/ksVJmObFibQ== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-aws-lambda@0.50.2": - version "0.50.2" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.50.2.tgz#b25a8c4644e6947c282c65b5844617b79a1ca90a" - integrity sha512-jz1a7t2q0SsiztEMyZjFLEFC4pOQ+1C588gWzl878k9Qr6TI1Wu3sa7/dikxJmeRIETcOTUilaa2Otxh6HUVlA== +"@opentelemetry/instrumentation-aws-lambda@0.50.3": + version "0.50.3" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-lambda/-/instrumentation-aws-lambda-0.50.3.tgz#bf76bd137780004aecfbb5c8335482afe5739878" + integrity sha512-kotm/mRvSWUauudxcylc5YCDei+G/r+jnOH6q5S99aPLQ/Ms8D2yonMIxEJUILIPlthEmwLYxkw3ualWzMjm/A== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" - "@types/aws-lambda" "8.10.143" + "@types/aws-lambda" "8.10.147" -"@opentelemetry/instrumentation-aws-sdk@0.49.0": - version "0.49.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.49.0.tgz#939a2af89bfd7d4c21a7ff0f0b42bac21793b42f" - integrity sha512-m3yC3ni4Yo8tggbZgygS/ccAP9e/EYqsMwzooHiIymbnyZwDAB7kMZ3OrjcLVPCFx9gjNMDKW4MdwOPC0vTEeQ== +"@opentelemetry/instrumentation-aws-sdk@0.49.1": + version "0.49.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-aws-sdk/-/instrumentation-aws-sdk-0.49.1.tgz#e5de7235af82a5b77eca2132da62d41d64dbbba9" + integrity sha512-Vbj4BYeV/1K4Pbbfk+gQ8gwYL0w+tBeUwG88cOxnF7CLPO1XnskGV8Q3Gzut2Ah/6Dg17dBtlzEqL3UiFP2Z6A== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.57.0" - "@opentelemetry/propagation-utils" "^0.30.15" + "@opentelemetry/instrumentation" "^0.57.1" + "@opentelemetry/propagation-utils" "^0.30.16" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-connect@0.43.0": - version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.43.0.tgz#411035f4a8f2e498dbfa7300e545c58586a062e2" - integrity sha512-Q57JGpH6T4dkYHo9tKXONgLtxzsh1ZEW5M9A/OwKrZFyEpLqWgjhcZ3hIuVvDlhb426iDF1f9FPToV/mi5rpeA== +"@opentelemetry/instrumentation-connect@0.43.1": + version "0.43.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-connect/-/instrumentation-connect-0.43.1.tgz#8ce88b94ce211c7bbdc9bd984b7a37876061bde3" + integrity sha512-ht7YGWQuV5BopMcw5Q2hXn3I8eG8TH0J/kc/GMcW4CuNTgiP6wCu44BOnucJWL3CmFWaRHI//vWyAhaC8BwePw== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" - "@types/connect" "3.4.36" + "@types/connect" "3.4.38" -"@opentelemetry/instrumentation-dataloader@0.16.0": - version "0.16.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.16.0.tgz#913345c335f67bf8e17a9b38c227dba741fe488b" - integrity sha512-88+qCHZC02up8PwKHk0UQKLLqGGURzS3hFQBZC7PnGwReuoKjHXS1o29H58S+QkXJpkTr2GACbx8j6mUoGjNPA== +"@opentelemetry/instrumentation-dataloader@0.16.1": + version "0.16.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-dataloader/-/instrumentation-dataloader-0.16.1.tgz#5d1d2c79f067c3102df7101f1753060ed93a1566" + integrity sha512-K/qU4CjnzOpNkkKO4DfCLSQshejRNAJtd4esgigo/50nxCB6XCyi1dhAblUHM9jG5dRm8eu0FB+t87nIo99LYQ== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" -"@opentelemetry/instrumentation-express@0.47.0": - version "0.47.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.47.0.tgz#f0477db3b1f4b342beb9ecd08edc26c470566724" - integrity sha512-XFWVx6k0XlU8lu6cBlCa29ONtVt6ADEjmxtyAyeF2+rifk8uBJbk1La0yIVfI0DoKURGbaEDTNelaXG9l/lNNQ== +"@opentelemetry/instrumentation-express@0.47.1": + version "0.47.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-express/-/instrumentation-express-0.47.1.tgz#7cf74f35e43cc3c8186edd1249fdb225849c48b2" + integrity sha512-QNXPTWteDclR2B4pDFpz0TNghgB33UMjUt14B+BZPmtH1MwUFAfLHBaP5If0Z5NZC+jaH8oF2glgYjrmhZWmSw== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-fastify@0.44.1": - version "0.44.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.44.1.tgz#c8080f24a6fbdd14689c619ad7b14fe189b10f28" - integrity sha512-RoVeMGKcNttNfXMSl6W4fsYoCAYP1vi6ZAWIGhBY+o7R9Y0afA7f9JJL0j8LHbyb0P0QhSYk+6O56OwI2k4iRQ== +"@opentelemetry/instrumentation-fastify@0.44.2": + version "0.44.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fastify/-/instrumentation-fastify-0.44.2.tgz#80bb33fa266560b0a7474f7bebcdb77eb49fc1c3" + integrity sha512-arSp97Y4D2NWogoXRb8CzFK3W2ooVdvqRRtQDljFt9uC3zI6OuShgey6CVFC0JxT1iGjkAr1r4PDz23mWrFULQ== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-fs@0.19.0": - version "0.19.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.19.0.tgz#a44807aea97edc64c597d6a5b5b8637b7ab45057" - integrity sha512-JGwmHhBkRT2G/BYNV1aGI+bBjJu4fJUD/5/Jat0EWZa2ftrLV3YE8z84Fiij/wK32oMZ88eS8DI4ecLGZhpqsQ== +"@opentelemetry/instrumentation-fs@0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-fs/-/instrumentation-fs-0.19.1.tgz#ebfe40781949574a66a82b8511d9bcd414dbfe98" + integrity sha512-6g0FhB3B9UobAR60BGTcXg4IHZ6aaYJzp0Ki5FhnxyAPt8Ns+9SSvgcrnsN2eGmk3RWG5vYycUGOEApycQL24A== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" -"@opentelemetry/instrumentation-generic-pool@0.43.0": - version "0.43.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.43.0.tgz#b1769eb0e30f2abb764a9cbc811aa3d4560ecc24" - integrity sha512-at8GceTtNxD1NfFKGAuwtqM41ot/TpcLh+YsGe4dhf7gvv1HW/ZWdq6nfRtS6UjIvZJOokViqLPJ3GVtZItAnQ== +"@opentelemetry/instrumentation-generic-pool@0.43.1": + version "0.43.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-generic-pool/-/instrumentation-generic-pool-0.43.1.tgz#6d1e181b32debc9510bdbbd63fe4ce5bc310d577" + integrity sha512-M6qGYsp1cURtvVLGDrPPZemMFEbuMmCXgQYTReC/IbimV5sGrLBjB+/hANUpRZjX67nGLdKSVLZuQQAiNz+sww== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" -"@opentelemetry/instrumentation-graphql@0.47.0": - version "0.47.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.47.0.tgz#271807e21a6224bd1986a3e9887650f1858ee733" - integrity sha512-Cc8SMf+nLqp0fi8oAnooNEfwZWFnzMiBHCGmDFYqmgjPylyLmi83b+NiTns/rKGwlErpW0AGPt0sMpkbNlzn8w== +"@opentelemetry/instrumentation-graphql@0.47.1": + version "0.47.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-graphql/-/instrumentation-graphql-0.47.1.tgz#1037bb546c82060d6d5d6f5dbd8765e31ccf6c26" + integrity sha512-EGQRWMGqwiuVma8ZLAZnExQ7sBvbOx0N/AE/nlafISPs8S+QtXX+Viy6dcQwVWwYHQPAcuY3bFt3xgoAwb4ZNQ== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" -"@opentelemetry/instrumentation-hapi@0.45.1": - version "0.45.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.45.1.tgz#5edf982549070d95e20152d568279548ad44d662" - integrity sha512-VH6mU3YqAKTePPfUPwfq4/xr049774qWtfTuJqVHoVspCLiT3bW+fCQ1toZxt6cxRPYASoYaBsMA3CWo8B8rcw== +"@opentelemetry/instrumentation-hapi@0.45.2": + version "0.45.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-hapi/-/instrumentation-hapi-0.45.2.tgz#14d670e0bbbdf864187a9f80265a9219ed2d01cf" + integrity sha512-7Ehow/7Wp3aoyCrZwQpU7a2CnoMq0XhIcioFuKjBb0PLYfBfmTsFTUyatlHu0fRxhwcRsSQRTvEhmZu8CppBpQ== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-http@0.57.1": - version "0.57.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.1.tgz#2d8b395df62191475e76fa0eb7bf60079ea886b9" - integrity sha512-ThLmzAQDs7b/tdKI3BV2+yawuF09jF111OFsovqT1Qj3D8vjwKBwhi/rDE5xethwn4tSXtZcJ9hBsVAlWFQZ7g== +"@opentelemetry/instrumentation-http@0.57.2": + version "0.57.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-http/-/instrumentation-http-0.57.2.tgz#f425eda67b6241c3abe08e4ea972169b85ef3064" + integrity sha512-1Uz5iJ9ZAlFOiPuwYg29Bf7bJJc/GeoeJIFKJYQf67nTVKFe8RHbEtxgkOmK4UGZNHKXcpW4P8cWBYzBn1USpg== dependencies: "@opentelemetry/core" "1.30.1" - "@opentelemetry/instrumentation" "0.57.1" + "@opentelemetry/instrumentation" "0.57.2" "@opentelemetry/semantic-conventions" "1.28.0" forwarded-parse "2.1.2" semver "^7.5.2" -"@opentelemetry/instrumentation-ioredis@0.47.0": - version "0.47.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.47.0.tgz#f83bd133d36d137d2d0b58bfbdfe12ed6fe5ab2f" - integrity sha512-4HqP9IBC8e7pW9p90P3q4ox0XlbLGme65YTrA3UTLvqvo4Z6b0puqZQP203YFu8m9rE/luLfaG7/xrwwqMUpJw== +"@opentelemetry/instrumentation-ioredis@0.47.1": + version "0.47.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-ioredis/-/instrumentation-ioredis-0.47.1.tgz#5cedd0ebe8cfd3569513a9b44945827bf844b331" + integrity sha512-OtFGSN+kgk/aoKgdkKQnBsQFDiG8WdCxu+UrHr0bXScdAmtSzLSraLo7wFIb25RVHfRWvzI5kZomqJYEg/l1iA== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/redis-common" "^0.36.2" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-kafkajs@0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.7.0.tgz#079b949ec814b42e49d23bb4d4f73735fe460d52" - integrity sha512-LB+3xiNzc034zHfCtgs4ITWhq6Xvdo8bsq7amR058jZlf2aXXDrN9SV4si4z2ya9QX4tz6r4eZJwDkXOp14/AQ== +"@opentelemetry/instrumentation-kafkajs@0.7.1": + version "0.7.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-kafkajs/-/instrumentation-kafkajs-0.7.1.tgz#cc7a31a5fe2c14171611da8e46827f762f332625" + integrity sha512-OtjaKs8H7oysfErajdYr1yuWSjMAectT7Dwr+axIoZqT9lmEOkD/H/3rgAs8h/NIuEi2imSXD+vL4MZtOuJfqQ== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-knex@0.44.0": - version "0.44.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.44.0.tgz#af251ed38f06a2f248812c5addf0266697b6149a" - integrity sha512-SlT0+bLA0Lg3VthGje+bSZatlGHw/vwgQywx0R/5u9QC59FddTQSPJeWNw29M6f8ScORMeUOOTwihlQAn4GkJQ== +"@opentelemetry/instrumentation-knex@0.44.1": + version "0.44.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-knex/-/instrumentation-knex-0.44.1.tgz#72f4efd798695c077ab218045d4c682231fbb36a" + integrity sha512-U4dQxkNhvPexffjEmGwCq68FuftFK15JgUF05y/HlK3M6W/G2iEaACIfXdSnwVNe9Qh0sPfw8LbOPxrWzGWGMQ== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-koa@0.47.0": - version "0.47.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.47.0.tgz#a74b35809ba95d0f9db49e8c3f214bde475b095a" - integrity sha512-HFdvqf2+w8sWOuwtEXayGzdZ2vWpCKEQv5F7+2DSA74Te/Cv4rvb2E5So5/lh+ok4/RAIPuvCbCb/SHQFzMmbw== +"@opentelemetry/instrumentation-koa@0.47.1": + version "0.47.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-koa/-/instrumentation-koa-0.47.1.tgz#ba57eccd44a75ec59e3129757fda4e8c8dd7ce2c" + integrity sha512-l/c+Z9F86cOiPJUllUCt09v+kICKvT+Vg1vOAJHtHPsJIzurGayucfCMq2acd/A/yxeNWunl9d9eqZ0G+XiI6A== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-lru-memoizer@0.44.0": - version "0.44.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.44.0.tgz#c22e770d950c165f80c657a9c790c9843baaa65b" - integrity sha512-Tn7emHAlvYDFik3vGU0mdwvWJDwtITtkJ+5eT2cUquct6nIs+H8M47sqMJkCpyPe5QIBJoTOHxmc6mj9lz6zDw== +"@opentelemetry/instrumentation-lru-memoizer@0.44.1": + version "0.44.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-lru-memoizer/-/instrumentation-lru-memoizer-0.44.1.tgz#1f0ec28130f8c379d310dc531a8b25780be8e445" + integrity sha512-5MPkYCvG2yw7WONEjYj5lr5JFehTobW7wX+ZUFy81oF2lr9IPfZk9qO+FTaM0bGEiymwfLwKe6jE15nHn1nmHg== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" -"@opentelemetry/instrumentation-mongodb@0.51.0": - version "0.51.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.51.0.tgz#8a323c2fb4cb2c93bf95f1b1c0fcb30952d12a08" - integrity sha512-cMKASxCX4aFxesoj3WK8uoQ0YUrRvnfxaO72QWI2xLu5ZtgX/QvdGBlU3Ehdond5eb74c2s1cqRQUIptBnKz1g== +"@opentelemetry/instrumentation-mongodb@0.52.0": + version "0.52.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongodb/-/instrumentation-mongodb-0.52.0.tgz#a5ed123f3fac5d7d08347353cd37d9cf00893746" + integrity sha512-1xmAqOtRUQGR7QfJFfGV/M2kC7wmI2WgZdpru8hJl3S0r4hW0n3OQpEHlSGXJAaNFyvT+ilnwkT+g5L4ljHR6g== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-mongoose@0.46.0": - version "0.46.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.46.0.tgz#c3a5f69e1a5b950b542cf84650fbbd3e31bd681e" - integrity sha512-mtVv6UeaaSaWTeZtLo4cx4P5/ING2obSqfWGItIFSunQBrYROfhuVe7wdIrFUs2RH1tn2YYpAJyMaRe/bnTTIQ== +"@opentelemetry/instrumentation-mongoose@0.46.1": + version "0.46.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mongoose/-/instrumentation-mongoose-0.46.1.tgz#23f22b7d4d5a548ac8add2a52ec2fec4e61c7de1" + integrity sha512-3kINtW1LUTPkiXFRSSBmva1SXzS/72we/jL22N+BnF3DFcoewkdkHPYOIdAAk9gSicJ4d5Ojtt1/HeibEc5OQg== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-mysql2@0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.45.0.tgz#95501759d470dbc7038670e91205e8ed601ec402" - integrity sha512-qLslv/EPuLj0IXFvcE3b0EqhWI8LKmrgRPIa4gUd8DllbBpqJAvLNJSv3cC6vWwovpbSI3bagNO/3Q2SuXv2xA== +"@opentelemetry/instrumentation-mysql2@0.45.2": + version "0.45.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql2/-/instrumentation-mysql2-0.45.2.tgz#590ed22f274a6999e57c3283433a119274cb572b" + integrity sha512-h6Ad60FjCYdJZ5DTz1Lk2VmQsShiViKe0G7sYikb0GHI0NVvApp2XQNRHNjEMz87roFttGPLHOYVPlfy+yVIhQ== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" "@opentelemetry/sql-common" "^0.40.1" -"@opentelemetry/instrumentation-mysql@0.45.0": - version "0.45.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.45.0.tgz#e4df8bc709c0c8b0ff90bbef92fb36e92ebe0d19" - integrity sha512-tWWyymgwYcTwZ4t8/rLDfPYbOTF3oYB8SxnYMtIQ1zEf5uDm90Ku3i6U/vhaMyfHNlIHvDhvJh+qx5Nc4Z3Acg== +"@opentelemetry/instrumentation-mysql@0.45.1": + version "0.45.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-mysql/-/instrumentation-mysql-0.45.1.tgz#6fb3fdf7b5afa62bfa4ce73fae213539bb660841" + integrity sha512-TKp4hQ8iKQsY7vnp/j0yJJ4ZsP109Ht6l4RHTj0lNEG1TfgTrIH5vJMbgmoYXWzNHAqBH2e7fncN12p3BP8LFg== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" "@types/mysql" "2.15.26" -"@opentelemetry/instrumentation-nestjs-core@0.44.0": - version "0.44.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.44.0.tgz#d2a3631de3bed2b1c0a03afa79c08ae22bef8b6c" - integrity sha512-t16pQ7A4WYu1yyQJZhRKIfUNvl5PAaF2pEteLvgJb/BWdd1oNuU1rOYt4S825kMy+0q4ngiX281Ss9qiwHfxFQ== +"@opentelemetry/instrumentation-nestjs-core@0.44.1": + version "0.44.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-nestjs-core/-/instrumentation-nestjs-core-0.44.1.tgz#54ee5877080055732093c59f8a9bc2aba4fae5f0" + integrity sha512-4TXaqJK27QXoMqrt4+hcQ6rKFd8B6V4JfrTJKnqBmWR1cbaqd/uwyl9yxhNH1JEkyo8GaBfdpBC4ZE4FuUhPmg== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-pg@0.51.0": - version "0.51.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.51.0.tgz#7d20016a32554ef66c008212dee3ff33fb1a587c" - integrity sha512-/NStIcUWUofc11dL7tSgMk25NqvhtbHDCncgm+yc4iJF8Ste2Q/lwUitjfxqj4qWM280uFmBEtcmtMMjbjRU7Q== +"@opentelemetry/instrumentation-pg@0.51.1": + version "0.51.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-pg/-/instrumentation-pg-0.51.1.tgz#a999a13fa56dc67da49a1ccf8f5e56a9ed409477" + integrity sha512-QxgjSrxyWZc7Vk+qGSfsejPVFL1AgAJdSBMYZdDUbwg730D09ub3PXScB9d04vIqPriZ+0dqzjmQx0yWKiCi2Q== dependencies: "@opentelemetry/core" "^1.26.0" - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" "@opentelemetry/sql-common" "^0.40.1" "@types/pg" "8.6.1" "@types/pg-pool" "2.0.6" -"@opentelemetry/instrumentation-redis-4@0.46.0": - version "0.46.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.46.0.tgz#828704b8134f023730ac508bcf3a38ca4d5d697c" - integrity sha512-aTUWbzbFMFeRODn3720TZO0tsh/49T8H3h8vVnVKJ+yE36AeW38Uj/8zykQ/9nO8Vrtjr5yKuX3uMiG/W8FKNw== +"@opentelemetry/instrumentation-redis-4@0.46.1": + version "0.46.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-redis-4/-/instrumentation-redis-4-0.46.1.tgz#325697dfccda3e70662769c6db230a37812697c6" + integrity sha512-UMqleEoabYMsWoTkqyt9WAzXwZ4BlFZHO40wr3d5ZvtjKCHlD4YXLm+6OLCeIi/HkX7EXvQaz8gtAwkwwSEvcQ== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/redis-common" "^0.36.2" "@opentelemetry/semantic-conventions" "^1.27.0" -"@opentelemetry/instrumentation-tedious@0.18.0": - version "0.18.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.18.0.tgz#636745423db28e303b4e0289b8f69685cb36f807" - integrity sha512-9zhjDpUDOtD+coeADnYEJQ0IeLVCj7w/hqzIutdp5NqS1VqTAanaEfsEcSypyvYv5DX3YOsTUoF+nr2wDXPETA== +"@opentelemetry/instrumentation-tedious@0.18.1": + version "0.18.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-tedious/-/instrumentation-tedious-0.18.1.tgz#d87dba9d0ddfc77f9fcbcceabcc31cb5a5f7bb11" + integrity sha512-5Cuy/nj0HBaH+ZJ4leuD7RjgvA844aY2WW+B5uLcWtxGjRZl3MNLuxnNg5DYWZNPO+NafSSnra0q49KWAHsKBg== dependencies: - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" "@opentelemetry/semantic-conventions" "^1.27.0" "@types/tedious" "^4.0.14" -"@opentelemetry/instrumentation-undici@0.10.0": - version "0.10.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.10.0.tgz#99cba213a6e9d47a82896b6c782c3f2d60e0edb5" - integrity sha512-vm+V255NGw9gaSsPD6CP0oGo8L55BffBc8KnxqsMuc6XiAD1L8SFNzsW0RHhxJFqy9CJaJh+YiJ5EHXuZ5rZBw== +"@opentelemetry/instrumentation-undici@0.10.1": + version "0.10.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation-undici/-/instrumentation-undici-0.10.1.tgz#228b7fc267e55533708be16c43e70bbb51a691de" + integrity sha512-rkOGikPEyRpMCmNu9AQuV5dtRlDmJp2dK5sw8roVshAGoB6hH/3QjDtRhdwd75SsJwgynWUNRUYe0wAkTo16tQ== dependencies: "@opentelemetry/core" "^1.8.0" - "@opentelemetry/instrumentation" "^0.57.0" + "@opentelemetry/instrumentation" "^0.57.1" -"@opentelemetry/instrumentation@0.57.1", "@opentelemetry/instrumentation@^0.57.0", "@opentelemetry/instrumentation@^0.57.1": - version "0.57.1" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.57.1.tgz#5aea772be8783a35d69d643da46582f381ba1810" - integrity sha512-SgHEKXoVxOjc20ZYusPG3Fh+RLIZTSa4x8QtD3NfgAUDyqdFFS9W1F2ZVbZkqDCdyMcQG02Ok4duUGLHJXHgbA== +"@opentelemetry/instrumentation@0.57.2", "@opentelemetry/instrumentation@^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0", "@opentelemetry/instrumentation@^0.57.1", "@opentelemetry/instrumentation@^0.57.2": + version "0.57.2" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.57.2.tgz#8924549d7941ba1b5c6f04d5529cf48330456d1d" + integrity sha512-BdBGhQBh8IjZ2oIIX6F2/Q3LKm/FDDKi6ccYKcBTeilh6SNdNKveDOLk73BkSJjQLJk6qe4Yh+hHw1UPhCDdrg== dependencies: - "@opentelemetry/api-logs" "0.57.1" - "@types/shimmer" "^1.2.0" - import-in-the-middle "^1.8.1" - require-in-the-middle "^7.1.1" - semver "^7.5.2" - shimmer "^1.2.1" - -"@opentelemetry/instrumentation@^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0": - version "0.56.0" - resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.56.0.tgz#3330ce16d9235a548efa1019a4a7f01414edd44a" - integrity sha512-2KkGBKE+FPXU1F0zKww+stnlUxUTlBvLCiWdP63Z9sqXYeNI/ziNzsxAp4LAdUcTQmXjw1IWgvm5CAb/BHy99w== - dependencies: - "@opentelemetry/api-logs" "0.56.0" + "@opentelemetry/api-logs" "0.57.2" "@types/shimmer" "^1.2.0" import-in-the-middle "^1.8.1" require-in-the-middle "^7.1.1" @@ -5880,10 +6139,10 @@ semver "^7.5.2" shimmer "^1.2.1" -"@opentelemetry/propagation-utils@^0.30.15": - version "0.30.15" - resolved "https://registry.yarnpkg.com/@opentelemetry/propagation-utils/-/propagation-utils-0.30.15.tgz#cf82691ec728afa6a117d0585da2b75ec7e0459a" - integrity sha512-nQ30K+eXTkd9Kt8yep9FPrqogS712GvdkV6R1T+xZMSZnFrRCyZuWxMtP3+s3hrK2HWw3ti4lsIfBzsHWYiyrA== +"@opentelemetry/propagation-utils@^0.30.16": + version "0.30.16" + resolved "https://registry.yarnpkg.com/@opentelemetry/propagation-utils/-/propagation-utils-0.30.16.tgz#6715d0225b618ea66cf34cc3800fa3452a8475fa" + integrity sha512-ZVQ3Z/PQ+2GQlrBfbMMMT0U7MzvYZLCPP800+ooyaBqm4hMvuQHfP028gB9/db0mwkmyEAMad9houukUVxhwcw== "@opentelemetry/redis-common@^0.36.2": version "0.36.2" @@ -5915,7 +6174,7 @@ "@opentelemetry/resources" "1.30.1" "@opentelemetry/semantic-conventions" "1.28.0" -"@opentelemetry/semantic-conventions@1.28.0", "@opentelemetry/semantic-conventions@^1.25.1", "@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.28.0": +"@opentelemetry/semantic-conventions@1.28.0": version "1.28.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz#337fb2bca0453d0726696e745f50064411f646d6" integrity sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA== @@ -5925,6 +6184,11 @@ resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-0.12.0.tgz#7e392aecdbdbd5d737d3995998b120dc17589ab0" integrity sha512-BuCcDW0uLNYYTns0/LwXkJ8lp8aDm7kpS+WunEmPAPRSCe6ciOYRvzn5reqJfX93rf+6A3U2SgrBnCTH+0qoQQ== +"@opentelemetry/semantic-conventions@^1.25.1", "@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.28.0", "@opentelemetry/semantic-conventions@^1.30.0": + version "1.30.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.30.0.tgz#3a42c4c475482f2ec87c12aad98832dc0087dc9a" + integrity sha512-4VlGgo32k2EQ2wcCY3vEU28A0O13aOtHz3Xt2/2U5FAh9EfhD6t6DqL5Z6yAnRCntbTFDU4YfbpyzSlHNWycPw== + "@opentelemetry/sql-common@^0.40.1": version "0.40.1" resolved "https://registry.yarnpkg.com/@opentelemetry/sql-common/-/sql-common-0.40.1.tgz#93fbc48d8017449f5b3c3274f2268a08af2b83b6" @@ -6066,12 +6330,12 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.28.tgz#d45e01c4a56f143ee69c54dd6b12eade9e270a73" integrity sha512-8LduaNlMZGwdZ6qWrKlfa+2M4gahzFkprZiAt2TF8uS0qQgBizKXpXURqvTJ4WtmupWxaLqjRb2UCTe72mu+Aw== -"@prisma/instrumentation@6.2.1": - version "6.2.1" - resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-6.2.1.tgz#261b885467d36759b7ca01d1b2ca4e1120bda886" - integrity sha512-QrcWRAwNHXX4nHXB+Q94nfm701gPQsR4zkaxYV6qCiENopRi8yYvXt6FNIvxbuwEiWW5Zid6DoWwIsBKJ/5r5w== +"@prisma/instrumentation@6.4.1": + version "6.4.1" + resolved "https://registry.yarnpkg.com/@prisma/instrumentation/-/instrumentation-6.4.1.tgz#3a0fb65bfb3e3a6712c41f9334599fa281c451d5" + integrity sha512-1SeN0IvMp5zm3RLJnEr+Zn67WDqUIPP1lF/PkLbi/X64vsnFyItcXNRBrYr0/sI2qLcH9iNzJUhyd3emdGizaQ== dependencies: - "@opentelemetry/instrumentation" "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0" + "@opentelemetry/instrumentation" "^0.52.0 || ^0.53.0 || ^0.54.0 || ^0.55.0 || ^0.56.0 || ^0.57.0" "@protobuf-ts/plugin-framework@^2.0.7", "@protobuf-ts/plugin-framework@^2.9.4": version "2.9.4" @@ -6162,6 +6426,51 @@ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== +"@react-router/dev@^7.1.5": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@react-router/dev/-/dev-7.2.0.tgz#f507dba3bd905653e2cff8b1521b9222e58360f5" + integrity sha512-GzSNGeWuhx6sMsnidCQAlCAephibUMC61xIAdsc6hBXWCJe/T9wUrvtnh2Xbcpr7BRZJtJN4UhI472ZURA6m9w== + dependencies: + "@babel/core" "^7.21.8" + "@babel/generator" "^7.21.5" + "@babel/parser" "^7.21.8" + "@babel/plugin-syntax-decorators" "^7.22.10" + "@babel/plugin-syntax-jsx" "^7.21.4" + "@babel/preset-typescript" "^7.21.5" + "@babel/traverse" "^7.23.2" + "@babel/types" "^7.22.5" + "@npmcli/package-json" "^4.0.1" + "@react-router/node" "7.2.0" + arg "^5.0.1" + babel-dead-code-elimination "^1.0.6" + chokidar "^4.0.0" + dedent "^1.5.3" + es-module-lexer "^1.3.1" + exit-hook "2.2.1" + fs-extra "^10.0.0" + gunzip-maybe "^1.4.2" + jsesc "3.0.2" + lodash "^4.17.21" + pathe "^1.1.2" + picocolors "^1.1.1" + picomatch "^2.3.1" + prettier "^2.7.1" + react-refresh "^0.14.0" + semver "^7.3.7" + set-cookie-parser "^2.6.0" + valibot "^0.41.0" + vite-node "3.0.0-beta.2" + +"@react-router/node@7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@react-router/node/-/node-7.2.0.tgz#c060130febe9db96112f29503bafc6cb82e7b8a6" + integrity sha512-CqBHLwvvV4BB8htmaSwT+SOwX9B4RVOIiEdTlaIp12sNVCGSYDIEGbv3T4Wxeq8p5ynNfhNcdBeXtZ6ZPWVozA== + dependencies: + "@mjackson/node-fetch-server" "^0.2.0" + source-map-support "^0.5.21" + stream-slice "^0.1.2" + undici "^6.19.2" + "@react-router/node@^7.1.5": version "7.1.5" resolved "https://registry.yarnpkg.com/@react-router/node/-/node-7.1.5.tgz#fe4bdb708bb574cbf21b359d1263f6accde737bd" @@ -6441,96 +6750,191 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.30.1.tgz#14c737dc19603a096568044eadaa60395eefb809" integrity sha512-pSWY+EVt3rJ9fQ3IqlrEUtXh3cGqGtPDH1FQlNZehO2yYxCHEX1SPsz1M//NXwYfbTlcKr9WObLnJX9FsS9K1Q== +"@rollup/rollup-android-arm-eabi@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz#731df27dfdb77189547bcef96ada7bf166bbb2fb" + integrity sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw== + "@rollup/rollup-android-arm64@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.30.1.tgz#9d81ea54fc5650eb4ebbc0a7d84cee331bfa30ad" integrity sha512-/NA2qXxE3D/BRjOJM8wQblmArQq1YoBVJjrjoTSBS09jgUisq7bqxNHJ8kjCHeV21W/9WDGwJEWSN0KQ2mtD/w== +"@rollup/rollup-android-arm64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz#4bea6db78e1f6927405df7fe0faf2f5095e01343" + integrity sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q== + "@rollup/rollup-darwin-arm64@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.30.1.tgz#29448cb1370cf678b50743d2e392be18470abc23" integrity sha512-r7FQIXD7gB0WJ5mokTUgUWPl0eYIH0wnxqeSAhuIwvnnpjdVB8cRRClyKLQr7lgzjctkbp5KmswWszlwYln03Q== +"@rollup/rollup-darwin-arm64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz#a7aab77d44be3c44a20f946e10160f84e5450e7f" + integrity sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q== + "@rollup/rollup-darwin-x64@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.30.1.tgz#0ca99741c3ed096700557a43bb03359450c7857d" integrity sha512-x78BavIwSH6sqfP2xeI1hd1GpHL8J4W2BXcVM/5KYKoAD3nNsfitQhvWSw+TFtQTLZ9OmlF+FEInEHyubut2OA== +"@rollup/rollup-darwin-x64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz#c572c024b57ee8ddd1b0851703ace9eb6cc0dd82" + integrity sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw== + "@rollup/rollup-freebsd-arm64@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.30.1.tgz#233f8e4c2f54ad9b719cd9645887dcbd12b38003" integrity sha512-HYTlUAjbO1z8ywxsDFWADfTRfTIIy/oUlfIDmlHYmjUP2QRDTzBuWXc9O4CXM+bo9qfiCclmHk1x4ogBjOUpUQ== +"@rollup/rollup-freebsd-arm64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz#cf74f8113b5a83098a5c026c165742277cbfb88b" + integrity sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA== + "@rollup/rollup-freebsd-x64@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.30.1.tgz#dfba762a023063dc901610722995286df4a48360" integrity sha512-1MEdGqogQLccphhX5myCJqeGNYTNcmTyaic9S7CG3JhwuIByJ7J05vGbZxsizQthP1xpVx7kd3o31eOogfEirw== +"@rollup/rollup-freebsd-x64@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz#39561f3a2f201a4ad6a01425b1ff5928154ecd7c" + integrity sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q== + "@rollup/rollup-linux-arm-gnueabihf@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.30.1.tgz#b9da54171726266c5ef4237f462a85b3c3cf6ac9" integrity sha512-PaMRNBSqCx7K3Wc9QZkFx5+CX27WFpAMxJNiYGAXfmMIKC7jstlr32UhTgK6T07OtqR+wYlWm9IxzennjnvdJg== +"@rollup/rollup-linux-arm-gnueabihf@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz#980d6061e373bfdaeb67925c46d2f8f9b3de537f" + integrity sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g== + "@rollup/rollup-linux-arm-musleabihf@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.30.1.tgz#b9db69b3f85f5529eb992936d8f411ee6d04297b" integrity sha512-B8Rcyj9AV7ZlEFqvB5BubG5iO6ANDsRKlhIxySXcF1axXYUyqwBok+XZPgIYGBgs7LDXfWfifxhw0Ik57T0Yug== +"@rollup/rollup-linux-arm-musleabihf@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz#f91a90f30dc00d5a64ac2d9bbedc829cd3cfaa78" + integrity sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA== + "@rollup/rollup-linux-arm64-gnu@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.30.1.tgz#2550cf9bb4d47d917fd1ab4af756d7bbc3ee1528" integrity sha512-hqVyueGxAj3cBKrAI4aFHLV+h0Lv5VgWZs9CUGqr1z0fZtlADVV1YPOij6AhcK5An33EXaxnDLmJdQikcn5NEw== +"@rollup/rollup-linux-arm64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz#fac700fa5c38bc13a0d5d34463133093da4c92a0" + integrity sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A== + "@rollup/rollup-linux-arm64-musl@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.30.1.tgz#9d06b26d286c7dded6336961a2f83e48330e0c80" integrity sha512-i4Ab2vnvS1AE1PyOIGp2kXni69gU2DAUVt6FSXeIqUCPIR3ZlheMW3oP2JkukDfu3PsexYRbOiJrY+yVNSk9oA== +"@rollup/rollup-linux-arm64-musl@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz#f50ecccf8c78841ff6df1706bc4782d7f62bf9c3" + integrity sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q== + "@rollup/rollup-linux-loongarch64-gnu@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.30.1.tgz#e957bb8fee0c8021329a34ca8dfa825826ee0e2e" integrity sha512-fARcF5g296snX0oLGkVxPmysetwUk2zmHcca+e9ObOovBR++9ZPOhqFUM61UUZ2EYpXVPN1redgqVoBB34nTpQ== +"@rollup/rollup-linux-loongarch64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz#5869dc0b28242da6553e2b52af41374f4038cd6e" + integrity sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ== + "@rollup/rollup-linux-powerpc64le-gnu@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.30.1.tgz#e8585075ddfb389222c5aada39ea62d6d2511ccc" integrity sha512-GLrZraoO3wVT4uFXh67ElpwQY0DIygxdv0BNW9Hkm3X34wu+BkqrDrkcsIapAY+N2ATEbvak0XQ9gxZtCIA5Rw== +"@rollup/rollup-linux-powerpc64le-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz#5cdd9f851ce1bea33d6844a69f9574de335f20b1" + integrity sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw== + "@rollup/rollup-linux-riscv64-gnu@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.30.1.tgz#7d0d40cee7946ccaa5a4e19a35c6925444696a9e" integrity sha512-0WKLaAUUHKBtll0wvOmh6yh3S0wSU9+yas923JIChfxOaaBarmb/lBKPF0w/+jTVozFnOXJeRGZ8NvOxvk/jcw== +"@rollup/rollup-linux-riscv64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz#ef5dc37f4388f5253f0def43e1440ec012af204d" + integrity sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw== + "@rollup/rollup-linux-s390x-gnu@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.30.1.tgz#c2dcd8a4b08b2f2778eceb7a5a5dfde6240ebdea" integrity sha512-GWFs97Ruxo5Bt+cvVTQkOJ6TIx0xJDD/bMAOXWJg8TCSTEK8RnFeOeiFTxKniTc4vMIaWvCplMAFBt9miGxgkA== +"@rollup/rollup-linux-s390x-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz#7dbc3ccbcbcfb3e65be74538dfb6e8dd16178fde" + integrity sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA== + "@rollup/rollup-linux-x64-gnu@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.30.1.tgz#183637d91456877cb83d0a0315eb4788573aa588" integrity sha512-UtgGb7QGgXDIO+tqqJ5oZRGHsDLO8SlpE4MhqpY9Llpzi5rJMvrK6ZGhsRCST2abZdBqIBeXW6WPD5fGK5SDwg== +"@rollup/rollup-linux-x64-gnu@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz#5783fc0adcab7dc069692056e8ca8d83709855ce" + integrity sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA== + "@rollup/rollup-linux-x64-musl@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.30.1.tgz#036a4c860662519f1f9453807547fd2a11d5bb01" integrity sha512-V9U8Ey2UqmQsBT+xTOeMzPzwDzyXmnAoO4edZhL7INkwQcaW1Ckv3WJX3qrrp/VHaDkEWIBWhRwP47r8cdrOow== +"@rollup/rollup-linux-x64-musl@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz#00b6c29b298197a384e3c659910b47943003a678" + integrity sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ== + "@rollup/rollup-win32-arm64-msvc@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.30.1.tgz#51cad812456e616bfe4db5238fb9c7497e042a52" integrity sha512-WabtHWiPaFF47W3PkHnjbmWawnX/aE57K47ZDT1BXTS5GgrBUEpvOzq0FI0V/UYzQJgdb8XlhVNH8/fwV8xDjw== +"@rollup/rollup-win32-arm64-msvc@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz#cbfee01f1fe73791c35191a05397838520ca3cdd" + integrity sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ== + "@rollup/rollup-win32-ia32-msvc@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.30.1.tgz#661c8b3e4cd60f51deaa39d153aac4566e748e5e" integrity sha512-pxHAU+Zv39hLUTdQQHUVHf4P+0C47y/ZloorHpzs2SXMRqeAWmGghzAhfOlzFHHwjvgokdFAhC4V+6kC1lRRfw== +"@rollup/rollup-win32-ia32-msvc@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz#95cdbdff48fe6c948abcf6a1d500b2bd5ce33f62" + integrity sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w== + "@rollup/rollup-win32-x64-msvc@4.30.1": version "4.30.1" resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.30.1.tgz#73bf1885ff052b82fbb0f82f8671f73c36e9137c" integrity sha512-D6qjsXGcvhTjv0kI4fU8tUuBDF/Ueee4SVX79VfNDXZa64TfCW1Slkb6Z7O1p7vflqZjcmOVdZlqf8gvJxc6og== +"@rollup/rollup-win32-x64-msvc@4.34.8": + version "4.34.8" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz#4cdb2cfae69cdb7b1a3cc58778e820408075e928" + integrity sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g== + "@schematics/angular@14.2.13": version "14.2.13" resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-14.2.13.tgz#35ee9120a3ac07077bad169fa74fdf4ce4e193d7" @@ -6548,34 +6952,34 @@ detect-libc "^2.0.2" node-abi "^3.61.0" -"@sentry-internal/rrdom@2.31.0": - version "2.31.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.31.0.tgz#548773964167ec104d3cbb9d7a4b25103c091e06" - integrity sha512-6sCgyKZy0Jpkb0wQ2XYLNcJjCETfbjHJ5jroAm2mU1imoSBlAldtNTdMc0wk8MaX/0q5qkMlr78SiYFf7xo12Q== +"@sentry-internal/rrdom@2.33.0": + version "2.33.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrdom/-/rrdom-2.33.0.tgz#16231b907e7d8a30d2c8a4de247bf87f272ded02" + integrity sha512-amTHYKq0u1FBC3lu6MM2Jnx04xIyDpsPcCJ8Pvi1sTyA/+mkDvGYXHWyjxVVWvhIOT6dVhuvWD82KW1gksrJcg== dependencies: - "@sentry-internal/rrweb-snapshot" "2.31.0" + "@sentry-internal/rrweb-snapshot" "2.33.0" -"@sentry-internal/rrweb-snapshot@2.31.0": - version "2.31.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.31.0.tgz#7a86d02429a490f6367d7aead0548fad7e0c9487" - integrity sha512-uGyJPfmOaiSZOZyd5HFiI8aCd/pPOtrZB89BJBgdB63++wD9Fry8OoWvRORzTo+N+Squummkq3Iucjm/yGdbAw== +"@sentry-internal/rrweb-snapshot@2.33.0": + version "2.33.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-snapshot/-/rrweb-snapshot-2.33.0.tgz#2b984757bd44e1d89022875f79ae16162cea78a6" + integrity sha512-sDg+i4QQ/nq97S4dyzMG0kbcQDbhHWFaoT8UXDODle9HpfeyoKuLF9d+JcHRZq0PID3/EIsWztTKw/xk96H8wQ== -"@sentry-internal/rrweb-types@2.31.0": - version "2.31.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.31.0.tgz#daf766526efff760eb6020ddf22c6432db9ff6a6" - integrity sha512-rWNCU6KaYopmd+353KBbRuNftpqfI97GdNFeCmB1wwwV/S2CW/lVcEn1uzMwzs8blm3YL2i/O+ykONxecQj+BQ== +"@sentry-internal/rrweb-types@2.33.0": + version "2.33.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb-types/-/rrweb-types-2.33.0.tgz#10593725dac929a9e19b6071c516faff4c0a20d8" + integrity sha512-lxwBh+bqnKf2gOj2tX2fbgVk/njlNIQuM19FsRSqi+KIHY7F9RLEC/N7chtCMFbckex6kljKM4RIa4DfKLkHDA== dependencies: - "@sentry-internal/rrweb-snapshot" "2.31.0" + "@sentry-internal/rrweb-snapshot" "2.33.0" "@types/css-font-loading-module" "0.0.7" -"@sentry-internal/rrweb@2.31.0": - version "2.31.0" - resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.31.0.tgz#69c6f6a4304c4d7446c3a1cc1c9b044d5dc4f040" - integrity sha512-u1uobvl5qnjtdhL2lrzbpwROZagc5xT1P2lANXxrKpLOUpp2+jMSjq0Zt2TElj+2aHlqh4lkDsj/OIa/FBHnnQ== +"@sentry-internal/rrweb@2.33.0": + version "2.33.0" + resolved "https://registry.yarnpkg.com/@sentry-internal/rrweb/-/rrweb-2.33.0.tgz#f1de2a7aca1a739acdfe91983b679c3bc3a448af" + integrity sha512-vjc+duxt3dxz6KVR6p+/kNl9AukMXmb7i6MMUZDn1TKfdeP9tGJprWl9fTkAmwkyQjm8VEWULs/3JK/u9dou5g== dependencies: - "@sentry-internal/rrdom" "2.31.0" - "@sentry-internal/rrweb-snapshot" "2.31.0" - "@sentry-internal/rrweb-types" "2.31.0" + "@sentry-internal/rrdom" "2.33.0" + "@sentry-internal/rrweb-snapshot" "2.33.0" + "@sentry-internal/rrweb-types" "2.33.0" "@types/css-font-loading-module" "0.0.7" "@xstate/fsm" "^1.4.0" base64-arraybuffer "^1.0.1" @@ -6644,36 +7048,71 @@ resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.41.1.tgz#ca7e12bf1ad59bc2df35868ae98abc8869108efa" integrity sha512-7pS3pu/SuhE6jOn3wptstAg6B5nUP878O6s+2svT7b5fKNfYUi/6NPK6dAveh2Ca0rwVq40TO4YFJabWMgTpdQ== +"@sentry/cli-darwin@2.42.1": + version "2.42.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-darwin/-/cli-darwin-2.42.1.tgz#ad4323091e2bc530907b3018fea3d4e2b6d0516f" + integrity sha512-WZFsrzSWtsRK24SiTa+Xod+4Hjlw7xaggmM4lbuo0lISO1EQj+K29jyGX+Ku0qflO1qp1z32bSP/RlWx/1rBjg== + "@sentry/cli-linux-arm64@2.41.1": version "2.41.1" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.41.1.tgz#948e8af8290418b1562db3531db08e69e39d74bb" integrity sha512-EzYCEnnENBnS5kpNW+2dBcrPZn1MVfywh2joGVQZTpmgDL5YFJ59VOd+K0XuEwqgFI8BSNI14KXZ75s4DD1/Vw== +"@sentry/cli-linux-arm64@2.42.1": + version "2.42.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.42.1.tgz#5f30014bb316da5e68c16a0b7bbccba48c1626f4" + integrity sha512-8A43bLvDIzquCXblHNadaRm109ANw1Q9VRXg5qLYv7DrPkUm2oQP+oRnuNUgOJ3W/8QQSvANpG9pPko+mJs4xw== + "@sentry/cli-linux-arm@2.41.1": version "2.41.1" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.41.1.tgz#1e5fa971ae8dfb3ea5564c8503b4e635ae6aed8a" integrity sha512-wNUvquD6qjOCczvuBGf9OiD29nuQ6yf8zzfyPJa5Bdx1QXuteKsKb6HBrMwuIR3liyuu0duzHd+H/+p1n541Hg== +"@sentry/cli-linux-arm@2.42.1": + version "2.42.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-arm/-/cli-linux-arm-2.42.1.tgz#4dfd3bcc5d40da8a45a045ccc178ed1ee1fe16f2" + integrity sha512-3xR2B9v8e7NjB6U9+oMu2puR3xOv/Axd7qNuUrZxQnNZYtgtnAqIDgSmFTWHOOoged1+AZXe+xDWLN0Y11Q03Q== + "@sentry/cli-linux-i686@2.41.1": version "2.41.1" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.41.1.tgz#3f01aff314f2ad8fd761f3e6e807a5ec09ae4eb4" integrity sha512-urpQCWrdYnSAsZY3udttuMV88wTJzKZL10xsrp7sjD/Hd+O6qSLVLkxebIlxts70jMLLFHYrQ2bkRg5kKuX6Fg== +"@sentry/cli-linux-i686@2.42.1": + version "2.42.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-i686/-/cli-linux-i686-2.42.1.tgz#b7646f19c922834c775f699b8acd320e11449735" + integrity sha512-YBz6prKqh1i0gzTg3Rus8ALQWmAk5Acap2U2dGuVYgTt7Bbu6SJbxNC9d8j3RUGu7ylupofUEMqKd391mTHf7g== + "@sentry/cli-linux-x64@2.41.1": version "2.41.1" resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.41.1.tgz#30dbf966a4b4c1721ffccd901dfcb6f967db073d" integrity sha512-ZqpYwHXAaK4MMEFlyaLYr6mJTmpy9qP6n30jGhLTW7kHKS3s6GPLCSlNmIfeClrInEt0963fM633ZRnXa04VPw== +"@sentry/cli-linux-x64@2.42.1": + version "2.42.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-linux-x64/-/cli-linux-x64-2.42.1.tgz#6ecb98811b351993cfb38afb7ae2c0ed6a23e0f2" + integrity sha512-Rvc6Jy3kLZrcyO7Ysy1gj0iQi0nGVUN79VqC3OO9JDV44aDtKBDYuBkeFKE3gd1SL8EvPetKH85en2u2wdWxYg== + "@sentry/cli-win32-i686@2.41.1": version "2.41.1" resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.41.1.tgz#f88eeb5d2d4ee46c38d8616ae1eb484108ea71c2" integrity sha512-AuRimCeVsx99DIOr9cwdYBHk39tlmAuPDdy2r16iNzY0InXs4xOys4gGzM7N4vlFQvFkzuc778Su0HkfasgprA== +"@sentry/cli-win32-i686@2.42.1": + version "2.42.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-i686/-/cli-win32-i686-2.42.1.tgz#fd2b6d990ef514844fd8416556cbf035cc67926b" + integrity sha512-FC8FE6dk+G83PCO09Ux/9NJNouF5yXKhpzLV5BZkqQye39hV9GDrFTu+VWTnwI1P77fnaJkPEEKRkjwNiPGjLA== + "@sentry/cli-win32-x64@2.41.1": version "2.41.1" resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.41.1.tgz#eefd95a2aa184adb464334e265b55a9142070f6f" integrity sha512-6JcPvXGye61+wPp0xdzfc2YLE/Dcud8JdaK8VxLM3b/8+Em7E+UyliDu3uF8+YGUqizY5JYTd3fs17DC8DZhLw== +"@sentry/cli-win32-x64@2.42.1": + version "2.42.1" + resolved "https://registry.yarnpkg.com/@sentry/cli-win32-x64/-/cli-win32-x64-2.42.1.tgz#acc8ff57802186f1e8686d82122f2a6a13ec5076" + integrity sha512-1595wD7JQSu5J9pA4m/B3WrjjIXltSV9VzuErehvanBvfusQ/YgBcvsNzgIf8aJsgSAYGbpR3Zqu81pjohdjgA== + "@sentry/cli@2.41.1", "@sentry/cli@^2.36.1", "@sentry/cli@^2.41.1": version "2.41.1" resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.41.1.tgz#a9467ca3ff4acfcdedec1565c9ff726b93758d29" @@ -6693,6 +7132,25 @@ "@sentry/cli-win32-i686" "2.41.1" "@sentry/cli-win32-x64" "2.41.1" +"@sentry/cli@^2.42.1": + version "2.42.1" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-2.42.1.tgz#f3d09eadd19ed5fe268bc06f507a08e2b544f350" + integrity sha512-3fonGZoGwlze/iGYDdCJXpG5skXc6j/yYom+k6TqVvJJqSct1RgLJHjCw1P0IxHsR8pNz9f1H85OdLXKxrc6sw== + dependencies: + https-proxy-agent "^5.0.0" + node-fetch "^2.6.7" + progress "^2.0.3" + proxy-from-env "^1.1.0" + which "^2.0.2" + optionalDependencies: + "@sentry/cli-darwin" "2.42.1" + "@sentry/cli-linux-arm" "2.42.1" + "@sentry/cli-linux-arm64" "2.42.1" + "@sentry/cli-linux-i686" "2.42.1" + "@sentry/cli-linux-x64" "2.42.1" + "@sentry/cli-win32-i686" "2.42.1" + "@sentry/cli-win32-x64" "2.42.1" + "@sentry/rollup-plugin@3.1.2": version "3.1.2" resolved "https://registry.yarnpkg.com/@sentry/rollup-plugin/-/rollup-plugin-3.1.2.tgz#d1ed4eeb558e10260bf0e7f292f9ad6baf22a98c" @@ -6709,7 +7167,7 @@ "@sentry/bundler-plugin-core" "2.22.6" unplugin "1.0.1" -"@sentry/vite-plugin@3.2.0": +"@sentry/vite-plugin@3.2.0", "@sentry/vite-plugin@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@sentry/vite-plugin/-/vite-plugin-3.2.0.tgz#0785b6e04e0aed8a4d6b57a433a2da11c14e6cd0" integrity sha512-IVBoAzZmpoX9+mnmIMq2ndxlFPoWMuYSE5Mek5zOWpYh+GbPxvkrxvM+vg0HeLH4r5v9Tm0FWcEZDgDIZqtoSg== @@ -7506,10 +7964,10 @@ resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.4.tgz#1a31c3d378850d2778dabb6374d036dcba4ba708" integrity sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw== -"@types/aws-lambda@8.10.143", "@types/aws-lambda@^8.10.62": - version "8.10.143" - resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.143.tgz#383693fbaadc6994a71d64a7c09e8c244fad8dff" - integrity sha512-u5vzlcR14ge/4pMTTMDQr3MF0wEe38B2F9o84uC4F43vN5DGTy63npRrB6jQhyt+C0lGv4ZfiRcRkqJoZuPnmg== +"@types/aws-lambda@8.10.147", "@types/aws-lambda@^8.10.62": + version "8.10.147" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.147.tgz#dc5c89aa32f47a9b35e52c32630545c83afa6f2f" + integrity sha512-nD0Z9fNIZcxYX5Mai2CTmFD7wX7UldCkW2ezCF8D1T5hdiLsnTWDGRpfRYntU6VjTdLQjOvyszru7I1c1oCQew== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.20.1", "@types/babel__core@^7.20.4": version "7.20.5" @@ -7593,10 +8051,10 @@ "@types/express-serve-static-core" "*" "@types/node" "*" -"@types/connect@*", "@types/connect@3.4.36": - version "3.4.36" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.36.tgz#e511558c15a39cb29bd5357eebb57bd1459cd1ab" - integrity sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w== +"@types/connect@*", "@types/connect@3.4.38": + version "3.4.38" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== dependencies: "@types/node" "*" @@ -7930,7 +8388,12 @@ dependencies: "@types/unist" "*" -"@types/history-4@npm:@types/history@4.7.8", "@types/history-5@npm:@types/history@4.7.8": +"@types/history-4@npm:@types/history@4.7.8": + version "4.7.8" + resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" + integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== + +"@types/history-5@npm:@types/history@4.7.8": version "4.7.8" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.8.tgz#49348387983075705fe8f4e02fb67f7daaec4934" integrity sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA== @@ -9441,7 +9904,7 @@ acorn@8.11.3: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== -acorn@8.12.1, acorn@^8.0.4, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.11.0, acorn@^8.11.3, acorn@^8.12.1, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.6.0, acorn@^8.7.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: +acorn@8.12.1: version "8.12.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.12.1.tgz#71616bdccbe25e27a54439e0046e89ca76df2248" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== @@ -9451,6 +9914,11 @@ acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.10.0, acorn@^8.11.0, acorn@^8.11.3, acorn@^8.12.1, acorn@^8.14.0, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.6.0, acorn@^8.7.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.1, acorn@^8.8.2, acorn@^8.9.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" @@ -9863,6 +10331,11 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== +arg@^5.0.1: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -10299,6 +10772,16 @@ b4a@^1.6.4: resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9" integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw== +babel-dead-code-elimination@^1.0.6: + version "1.0.9" + resolved "https://registry.yarnpkg.com/babel-dead-code-elimination/-/babel-dead-code-elimination-1.0.9.tgz#c994291aeef33ad1d535cf44577c582da62cda44" + integrity sha512-JLIhax/xullfInZjtu13UJjaLHDeTzt3vOeomaSUdO/nAMEL/pWC/laKrSvWylXMnVWyL5bpmG9njqBZlUQOdg== + dependencies: + "@babel/core" "^7.23.7" + "@babel/parser" "^7.23.6" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" + babel-eslint@~10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" @@ -11339,6 +11822,13 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== +browserify-zlib@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" + integrity sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ== + dependencies: + pako "~0.2.0" + browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.20.0, browserslist@^4.21.10, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.23.0, browserslist@^4.23.3, browserslist@^4.24.0, browserslist@^4.9.1: version "4.24.2" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.2.tgz#f5845bc91069dbd55ee89faf9822e1d885d16580" @@ -11840,6 +12330,13 @@ check-error@^2.1.1: optionalDependencies: fsevents "~2.3.2" +chokidar@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" + integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== + dependencies: + readdirp "^4.0.1" + chokidar@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41" @@ -13053,7 +13550,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@^4.3.7: +debug@4, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6, debug@^4.3.7, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -13138,6 +13635,11 @@ dedent@0.7.0, dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +dedent@^1.5.3: + version "1.5.3" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.3.tgz#99aee19eb9bae55a67327717b6e848d0bf777e5a" + integrity sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ== + deep-eql@^5.0.1: version "5.0.2" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" @@ -13717,6 +14219,16 @@ duplexer@^0.1.1, duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +duplexify@^3.5.0, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + duplexify@^4.0.0, duplexify@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.1.tgz#7027dc374f157b122a8ae08c2d3ea4d2d953aa61" @@ -14358,7 +14870,7 @@ encoding@^0.1.11, encoding@^0.1.13: dependencies: iconv-lite "^0.6.2" -end-of-stream@^1.1.0, end-of-stream@^1.4.1: +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== @@ -14557,7 +15069,7 @@ es-module-lexer@^0.9.0: resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19" integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== -es-module-lexer@^1.2.1, es-module-lexer@^1.3.0, es-module-lexer@^1.5.4: +es-module-lexer@^1.2.1, es-module-lexer@^1.3.0, es-module-lexer@^1.3.1, es-module-lexer@^1.5.4: version "1.6.0" resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.6.0.tgz#da49f587fd9e68ee2404fe4e256c0c7d3a81be21" integrity sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ== @@ -15044,6 +15556,37 @@ esbuild@^0.23.0, esbuild@^0.23.1: "@esbuild/win32-ia32" "0.23.1" "@esbuild/win32-x64" "0.23.1" +esbuild@^0.24.2: + version "0.24.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.24.2.tgz#b5b55bee7de017bff5fb8a4e3e44f2ebe2c3567d" + integrity sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA== + optionalDependencies: + "@esbuild/aix-ppc64" "0.24.2" + "@esbuild/android-arm" "0.24.2" + "@esbuild/android-arm64" "0.24.2" + "@esbuild/android-x64" "0.24.2" + "@esbuild/darwin-arm64" "0.24.2" + "@esbuild/darwin-x64" "0.24.2" + "@esbuild/freebsd-arm64" "0.24.2" + "@esbuild/freebsd-x64" "0.24.2" + "@esbuild/linux-arm" "0.24.2" + "@esbuild/linux-arm64" "0.24.2" + "@esbuild/linux-ia32" "0.24.2" + "@esbuild/linux-loong64" "0.24.2" + "@esbuild/linux-mips64el" "0.24.2" + "@esbuild/linux-ppc64" "0.24.2" + "@esbuild/linux-riscv64" "0.24.2" + "@esbuild/linux-s390x" "0.24.2" + "@esbuild/linux-x64" "0.24.2" + "@esbuild/netbsd-arm64" "0.24.2" + "@esbuild/netbsd-x64" "0.24.2" + "@esbuild/openbsd-arm64" "0.24.2" + "@esbuild/openbsd-x64" "0.24.2" + "@esbuild/sunos-x64" "0.24.2" + "@esbuild/win32-arm64" "0.24.2" + "@esbuild/win32-ia32" "0.24.2" + "@esbuild/win32-x64" "0.24.2" + escalade@^3.1.1, escalade@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" @@ -15553,7 +16096,7 @@ execa@^8.0.1: signal-exit "^4.1.0" strip-final-newline "^3.0.0" -exit-hook@^2.2.1: +exit-hook@2.2.1, exit-hook@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-2.2.1.tgz#007b2d92c6428eda2b76e7016a34351586934593" integrity sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw== @@ -16716,6 +17259,18 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +glob@11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.1.tgz#1c3aef9a59d680e611b53dcd24bb8639cef064d9" + integrity sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^4.0.1" + minimatch "^10.0.0" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + glob@7.1.4: version "7.1.4" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.4.tgz#aa608a2f6c577ad357e1ae5a5c26d9a8d1969255" @@ -17043,6 +17598,18 @@ gud@^1.0.0: resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== +gunzip-maybe@^1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz#b913564ae3be0eda6f3de36464837a9cd94b98ac" + integrity sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw== + dependencies: + browserify-zlib "^0.1.4" + is-deflate "^1.0.0" + is-gzip "^1.0.0" + peek-stream "^1.1.0" + pumpify "^1.3.3" + through2 "^2.0.3" + gzip-size@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-6.0.0.tgz#065367fd50c239c0671cbcbad5be3e2eeb10e462" @@ -17543,6 +18110,13 @@ hosted-git-info@^6.0.0: dependencies: lru-cache "^7.5.1" +hosted-git-info@^6.1.1: + version "6.1.3" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-6.1.3.tgz#2ee1a14a097a1236bddf8672c35b613c46c55946" + integrity sha512-HVJyzUrLIL1c0QmviVh5E8VGyUS7xCFPS6yydaVd1UegW+ibV/CohqTH9MkOLDp5o+rb82DMo77PTuc9F/8GKw== + dependencies: + lru-cache "^7.5.1" + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -17874,12 +18448,12 @@ import-fresh@^3.0.0, import-fresh@^3.2.1: parent-module "^1.0.0" resolve-from "^4.0.0" -import-in-the-middle@^1.12.0, import-in-the-middle@^1.8.1: - version "1.12.0" - resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.12.0.tgz#80d6536a01d0708a6f119f30d22447d4eb9e5c63" - integrity sha512-yAgSE7GmtRcu4ZUSFX/4v69UGXwugFFSdIQJ14LHPOPPQrWv8Y7O9PHsw8Ovk7bKCLe4sjXMbZFqGFcLHpZ89w== +import-in-the-middle@^1.13.0, import-in-the-middle@^1.8.1: + version "1.13.0" + resolved "https://registry.yarnpkg.com/import-in-the-middle/-/import-in-the-middle-1.13.0.tgz#e592583c3f53ff29c6079c0af31feab592ac6b2a" + integrity sha512-YG86SYDtrL/Yu8JgfWb7kjQ0myLeT1whw6fs/ZHFkXFcbk9zJU9lOCsSJHpvaPumU11nN3US7NW6x1YTk+HrUA== dependencies: - acorn "^8.8.2" + acorn "^8.14.0" acorn-import-attributes "^1.9.5" cjs-module-lexer "^1.2.2" module-details-from-path "^1.0.3" @@ -18279,6 +18853,11 @@ is-date-object@^1.0.1, is-date-object@^1.0.5: dependencies: has-tostringtag "^1.0.0" +is-deflate@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-deflate/-/is-deflate-1.0.0.tgz#c862901c3c161fb09dac7cdc7e784f80e98f2f14" + integrity sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ== + is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -18356,6 +18935,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-gzip@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" + integrity sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ== + is-inside-container@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-inside-container/-/is-inside-container-1.0.0.tgz#e81fba699662eb31dbdaf26766a61d4814717ea4" @@ -18841,6 +19425,13 @@ jackspeak@^3.1.2: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jackspeak@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.0.tgz#c489c079f2b636dc4cbe9b0312a13ff1282e561b" + integrity sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw== + dependencies: + "@isaacs/cliui" "^8.0.2" + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -19428,16 +20019,16 @@ jsdom@^21.1.2: ws "^8.13.0" xml-name-validator "^4.0.0" +jsesc@3.0.2, jsesc@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" + integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -jsesc@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" - integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== - jsesc@~0.3.x: version "0.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.3.0.tgz#1bf5ee63b4539fe2e26d0c1e99c240b97a457972" @@ -20475,6 +21066,11 @@ lru-cache@^10.2.0, lru-cache@^10.4.3: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^11.0.0: + version "11.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.2.tgz#fbd8e7cf8211f5e7e5d91905c415a3f55755ca39" + integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -21481,6 +22077,13 @@ minimatch@5.1.0, minimatch@^5.0.1, minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" +minimatch@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + minimatch@^7.4.1: version "7.4.6" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.6.tgz#845d6f254d8f4a5e4fd6baf44d5f10c8448365fb" @@ -21966,6 +22569,11 @@ nanoid@^3.3.3, nanoid@^3.3.4, nanoid@^3.3.6, nanoid@^3.3.7: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== +nanoid@^3.3.8: + version "3.3.8" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" + integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== + nanoid@^5.0.7: version "5.0.7" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.7.tgz#6452e8c5a816861fd9d2b898399f7e5fd6944cc6" @@ -23487,6 +24095,11 @@ pako@^1.0.3: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +pako@~0.2.0: + version "0.2.9" + resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" + integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" @@ -23695,6 +24308,14 @@ path-scurry@^1.11.1, path-scurry@^1.6.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + path-to-regexp@0.1.10: version "0.1.10" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" @@ -23756,6 +24377,15 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.0.tgz#7e2550b422601d4f6b8e26f1301bc8f15a741a25" integrity sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA== +peek-stream@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/peek-stream/-/peek-stream-1.1.3.tgz#3b35d84b7ccbbd262fff31dc10da56856ead6d67" + integrity sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA== + dependencies: + buffer-from "^1.0.0" + duplexify "^3.5.0" + through2 "^2.0.3" + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -24601,6 +25231,15 @@ postcss@^8.1.10, postcss@^8.2.14, postcss@^8.2.15, postcss@^8.3.7, postcss@^8.4. picocolors "^1.1.1" source-map-js "^1.2.1" +postcss@^8.5.2: + version "8.5.3" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" + integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== + dependencies: + nanoid "^3.3.8" + picocolors "^1.1.1" + source-map-js "^1.2.1" + postgres-array@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" @@ -24721,6 +25360,11 @@ prettier@^2.5.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== +prettier@^2.7.1: + version "2.8.8" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" + integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== + prettier@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.1.1.tgz#6ba9f23165d690b6cbdaa88cb0807278f7019848" @@ -24971,6 +25615,14 @@ pstree.remy@^1.1.8: resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== +pump@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -24979,6 +25631,15 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + punycode@^2.1.0, punycode@^2.1.1, punycode@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -25144,6 +25805,11 @@ react-is@^18.0.0: resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== +react-refresh@^0.14.0: + version "0.14.2" + resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9" + integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA== + "react-router-3@npm:react-router@3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/react-router/-/react-router-3.2.0.tgz#62b6279d589b70b34e265113e4c0a9261a02ed36" @@ -25365,7 +26031,7 @@ readable-stream@2.3.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.2.2, readable-stream@^2.3.5, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@^2.2.2, readable-stream@^2.3.5, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -26208,6 +26874,34 @@ rollup@^4.18.0, rollup@^4.20.0, rollup@^4.24.2: "@rollup/rollup-win32-x64-msvc" "4.30.1" fsevents "~2.3.2" +rollup@^4.30.1: + version "4.34.8" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.8.tgz#e859c1a51d899aba9bcf451d4eed1d11fb8e2a6e" + integrity sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.34.8" + "@rollup/rollup-android-arm64" "4.34.8" + "@rollup/rollup-darwin-arm64" "4.34.8" + "@rollup/rollup-darwin-x64" "4.34.8" + "@rollup/rollup-freebsd-arm64" "4.34.8" + "@rollup/rollup-freebsd-x64" "4.34.8" + "@rollup/rollup-linux-arm-gnueabihf" "4.34.8" + "@rollup/rollup-linux-arm-musleabihf" "4.34.8" + "@rollup/rollup-linux-arm64-gnu" "4.34.8" + "@rollup/rollup-linux-arm64-musl" "4.34.8" + "@rollup/rollup-linux-loongarch64-gnu" "4.34.8" + "@rollup/rollup-linux-powerpc64le-gnu" "4.34.8" + "@rollup/rollup-linux-riscv64-gnu" "4.34.8" + "@rollup/rollup-linux-s390x-gnu" "4.34.8" + "@rollup/rollup-linux-x64-gnu" "4.34.8" + "@rollup/rollup-linux-x64-musl" "4.34.8" + "@rollup/rollup-win32-arm64-msvc" "4.34.8" + "@rollup/rollup-win32-ia32-msvc" "4.34.8" + "@rollup/rollup-win32-x64-msvc" "4.34.8" + fsevents "~2.3.2" + rrweb-cssom@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz#ed298055b97cbddcdeb278f904857629dec5e0e1" @@ -26534,16 +27228,26 @@ serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" -seroval-plugins@^1.0.2, seroval-plugins@^1.0.3: +seroval-plugins@^1.0.2: version "1.0.7" resolved "https://registry.yarnpkg.com/seroval-plugins/-/seroval-plugins-1.0.7.tgz#c02511a1807e9bc8f68a91fbec13474fa9cea670" integrity sha512-GO7TkWvodGp6buMEX9p7tNyIkbwlyuAWbI6G9Ec5bhcm7mQdu3JOK1IXbEUwb3FVzSc363GraG/wLW23NSavIw== -seroval@^1.0.2, seroval@^1.0.4: +seroval-plugins@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/seroval-plugins/-/seroval-plugins-1.2.1.tgz#fa535e70ade8af553634b2b5c80d8a6fd8c2ff72" + integrity sha512-H5vs53+39+x4Udwp4J5rNZfgFuA+Lt+uU+09w1gYBVWomtAl98B+E9w7yC05Xc81/HgLvJdlyqJbU0fJCKCmdw== + +seroval@^1.0.2: version "1.0.7" resolved "https://registry.yarnpkg.com/seroval/-/seroval-1.0.7.tgz#ee48ad8ba69f1595bdd5c55d1a0d1da29dee7455" integrity sha512-n6ZMQX5q0Vn19Zq7CIKNIo7E75gPkGCFUEqDpa8jgwpYr/vScjqnQ6H09t1uIiZ0ZSK0ypEGvrYK2bhBGWsGdw== +seroval@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/seroval/-/seroval-1.2.1.tgz#fc671d63445923ab64f7abaf3967c83901382f40" + integrity sha512-yBxFFs3zmkvKNmR0pFSU//rIsYjuX418TnlDmc2weaq5XFDqDIV/NOMPBoLrbxjLH42p4UzRuXHryXh9dYcKcw== + serve-index@^1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" @@ -26991,13 +27695,13 @@ socks@^2.6.2: smart-buffer "^4.2.0" solid-js@^1.8.11: - version "1.8.17" - resolved "https://registry.yarnpkg.com/solid-js/-/solid-js-1.8.17.tgz#780ed6f0fd8633009d1b3c29d56bf6b6bb33bd50" - integrity sha512-E0FkUgv9sG/gEBWkHr/2XkBluHb1fkrHywUgA6o6XolPDCJ4g1HaLmQufcBBhiF36ee40q+HpG/vCZu7fLpI3Q== + version "1.9.5" + resolved "https://registry.yarnpkg.com/solid-js/-/solid-js-1.9.5.tgz#168ae067c27d3d437c868484d21751335ec16063" + integrity sha512-ogI3DaFcyn6UhYhrgcyRAMbu/buBJitYQASZz5WzfQVPP10RD2AbCoRZ517psnezrasyCbWzIxZ6kVqet768xw== dependencies: csstype "^3.1.0" - seroval "^1.0.4" - seroval-plugins "^1.0.3" + seroval "^1.1.0" + seroval-plugins "^1.1.0" solid-refresh@^0.6.3: version "0.6.3" @@ -27465,7 +28169,16 @@ string-template@~0.2.1: resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" integrity sha1-QpMuWYo1LQH8IuwzZ9nYTuxsmt0= -"string-width-cjs@npm:string-width@^4.2.0", string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@4.2.3, "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -27568,7 +28281,14 @@ stringify-object@^3.2.1: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -28176,7 +28896,7 @@ throttleit@2.1.0: resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-2.1.0.tgz#a7e4aa0bf4845a5bd10daa39ea0c783f631a07b4" integrity sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw== -through2@^2.0.0: +through2@^2.0.0, through2@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== @@ -28536,12 +29256,12 @@ tslib@2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== -tslib@2.7.0, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.6.2: +tslib@2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== -tslib@2.8.1: +tslib@2.8.1, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, tslib@^2.6.2: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -28866,12 +29586,7 @@ undici@^5.25.4, undici@^5.28.4: dependencies: "@fastify/busboy" "^2.0.0" -undici@^6.11.1: - version "6.21.0" - resolved "https://registry.yarnpkg.com/undici/-/undici-6.21.0.tgz#4b3d3afaef984e07b48e7620c34ed8a285ed4cd4" - integrity sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw== - -undici@^6.19.2: +undici@^6.11.1, undici@^6.19.2: version "6.21.1" resolved "https://registry.yarnpkg.com/undici/-/undici-6.21.1.tgz#336025a14162e6837e44ad7b819b35b6c6af0e05" integrity sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ== @@ -29437,6 +30152,11 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" +valibot@^0.41.0: + version "0.41.0" + resolved "https://registry.yarnpkg.com/valibot/-/valibot-0.41.0.tgz#5c2efd49c078e455f7862379365f6036f3cd9f96" + integrity sha512-igDBb8CTYr8YTQlOKgaN9nSS0Be7z+WRuaeYqGf3Cjz3aKmSnqEmYnkfVjzIuumGqfHpa3fLIvMEAfhrpqN8ng== + validate-html-nesting@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/validate-html-nesting/-/validate-html-nesting-1.2.2.tgz#2d74de14b598a0de671fad01bd71deabb93b8aca" @@ -29571,6 +30291,17 @@ vite-node@2.1.8, vite-node@^2.1.1: pathe "^1.1.2" vite "^5.0.0" +vite-node@3.0.0-beta.2: + version "3.0.0-beta.2" + resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-3.0.0-beta.2.tgz#4208a6be384f9e7bba97570114d662ce9c957dc1" + integrity sha512-ofTf6cfRdL30Wbl9n/BX81EyIR5s4PReLmSurrxQ+koLaWUNOEo8E0lCM53OJkb8vpa2URM2nSrxZsIFyvY1rg== + dependencies: + cac "^6.7.14" + debug "^4.4.0" + es-module-lexer "^1.5.4" + pathe "^1.1.2" + vite "^5.0.0 || ^6.0.0" + vite-plugin-checker@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/vite-plugin-checker/-/vite-plugin-checker-0.8.0.tgz#33419857a623b35c9483e4f603d4ca8b6984acde" @@ -29669,6 +30400,17 @@ vite@^5.0.0, vite@^5.4.11, vite@^5.4.5: optionalDependencies: fsevents "~2.3.3" +"vite@^5.0.0 || ^6.0.0", vite@^6.1.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.1.1.tgz#c1f221749298357b9230782a04483e60ad83c8db" + integrity sha512-4GgM54XrwRfrOp297aIYspIti66k56v16ZnqHvrIM7mG+HjDlAwS7p+Srr7J6fGvEdOJ5JcQ/D9T7HhtdXDTzA== + dependencies: + esbuild "^0.24.2" + postcss "^8.5.2" + rollup "^4.30.1" + optionalDependencies: + fsevents "~2.3.3" + vitefu@^0.2.2, vitefu@^0.2.4, vitefu@^0.2.5: version "0.2.5" resolved "https://registry.yarnpkg.com/vitefu/-/vitefu-0.2.5.tgz#c1b93c377fbdd3e5ddd69840ea3aa70b40d90969" @@ -30368,7 +31110,16 @@ wrangler@^3.67.1: optionalDependencies: fsevents "~2.3.2" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@7.0.0, wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@7.0.0, wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -30670,12 +31421,7 @@ zod-to-json-schema@^3.23.5: resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.23.5.tgz#ec23def47dcafe3a4d640eba6a346b34f9a693a5" integrity sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA== -zod@^3.22.3, zod@^3.22.4: - version "3.23.8" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d" - integrity sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g== - -zod@^3.24.1: +zod@^3.22.3, zod@^3.22.4, zod@^3.24.1: version "3.24.1" resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.1.tgz#27445c912738c8ad1e9de1bea0359fa44d9d35ee" integrity sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==