diff --git a/.github/CANARY_FAILURE_TEMPLATE.md b/.github/CANARY_FAILURE_TEMPLATE.md index 49461065bd34..d52932b93fc3 100644 --- a/.github/CANARY_FAILURE_TEMPLATE.md +++ b/.github/CANARY_FAILURE_TEMPLATE.md @@ -1,5 +1,5 @@ --- -title: Canary tests failed -labels: 'Status: Untriaged, Type: Tests' +title: '{{ env.TITLE }}' +labels: 'Type: Tests' --- Canary tests failed: {{ env.RUN_LINK }} diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 33885888b788..5495324f837d 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -32,6 +32,7 @@ body: options: - '@sentry/browser' - '@sentry/angular' + - '@sentry/angular-ivy' - '@sentry/ember' - '@sentry/gatsby' - '@sentry/nextjs' diff --git a/.github/ISSUE_TEMPLATE/feature.yml b/.github/ISSUE_TEMPLATE/feature.yml index 2492425a230a..7749deee1d44 100644 --- a/.github/ISSUE_TEMPLATE/feature.yml +++ b/.github/ISSUE_TEMPLATE/feature.yml @@ -1,6 +1,6 @@ name: 💡 Feature Request description: Create a feature request for a sentry-javascript SDK. -labels: 'Type: Improvement' +labels: ['Type: Improvement'] body: - type: markdown attributes: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a5523a861eee..696ae9310d11 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,6 +43,7 @@ env: ${{ github.workspace }}/packages/utils/esm BUILD_CACHE_KEY: ${{ github.event.inputs.commit || github.sha }} + BUILD_CACHE_TARBALL_KEY: tarball-${{ github.event.inputs.commit || github.sha }} # GH will use the first restore-key it finds that matches # So it will start by looking for one from the same branch, else take the newest one it can find elsewhere @@ -430,7 +431,7 @@ jobs: strategy: fail-fast: false matrix: - node: [10, 12, 14, 16, 18] + node: [10, 12, 14, 16, 18, 20] steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v3 @@ -706,7 +707,7 @@ jobs: strategy: fail-fast: false matrix: - node: [14, 16, 18] + node: [14, 16, 18, 20] remix: [1, 2] steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) @@ -729,20 +730,82 @@ jobs: cd packages/remix yarn test:integration:ci + job_e2e_prepare: + name: Prepare E2E tests + if: + (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] + runs-on: ubuntu-20.04 + timeout-minutes: 15 + steps: + - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) + uses: actions/checkout@v3 + with: + ref: ${{ env.HEAD_COMMIT }} + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version-file: 'package.json' + - name: Restore caches + uses: ./.github/actions/restore-cache + env: + DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Check tarball cache + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/packages/*/*.tgz + key: ${{ env.BUILD_CACHE_TARBALL_KEY }} + - name: Build tarballs + run: yarn build:tarball + job_e2e_tests: - name: E2E (Shard ${{ matrix.shard }}) Tests + name: E2E ${{ matrix.label || matrix.test-application }} Test # We only run E2E tests for non-fork PRs because the E2E tests require secrets to work and they can't be accessed from forks # Dependabot PRs sadly also don't have access to secrets, so we skip them as well if: (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] + needs: [job_get_metadata, job_build, job_e2e_prepare] runs-on: ubuntu-20.04 - timeout-minutes: 30 + timeout-minutes: 10 + env: + E2E_TEST_AUTH_TOKEN: ${{ secrets.E2E_TEST_AUTH_TOKEN }} + E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + # Needed because some apps expect a certain prefix + NEXT_PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + REACT_APP_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' + E2E_TEST_SENTRY_TEST_PROJECT: 'sentry-javascript-e2e-tests' strategy: fail-fast: false matrix: - shard: [1, 2, 3, 4] + test-application: + [ + 'node-express-app', + 'create-react-app', + 'create-next-app', + 'create-remix-app', + 'nextjs-app-dir', + 'react-create-hash-router', + 'standard-frontend-react', + 'standard-frontend-react-tracing-import', + 'sveltekit', + ] + build-command: + - false + label: + - false + # Add any variations of a test app here + # You should provide an alternate build-command as well as a matching label + include: + - test-application: 'create-react-app' + build-command: 'test:build-ts3.8' + label: 'create-react-app (TS 3.8)' + - test-application: 'standard-frontend-react' + build-command: 'test:build-ts3.8' + label: 'standard-frontend-react (TS 3.8)' steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) @@ -761,22 +824,36 @@ jobs: env: DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + - name: Restore tarball cache + uses: actions/cache/restore@v3 + with: + path: ${{ github.workspace }}/packages/*/*.tgz + key: ${{ env.BUILD_CACHE_TARBALL_KEY }} + - name: Get node version id: versions run: | echo "echo node=$(jq -r '.volta.node' package.json)" >> $GITHUB_OUTPUT - - name: Run E2E tests + + - name: Validate Verdaccio + run: yarn test:validate + working-directory: packages/e2e-tests + + - name: Prepare Verdaccio + run: yarn test:prepare + working-directory: packages/e2e-tests env: E2E_TEST_PUBLISH_SCRIPT_NODE_VERSION: ${{ steps.versions.outputs.node }} - E2E_TEST_AUTH_TOKEN: ${{ secrets.E2E_TEST_AUTH_TOKEN }} - E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} - E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' - E2E_TEST_SENTRY_TEST_PROJECT: 'sentry-javascript-e2e-tests' - E2E_TEST_SHARD: ${{ matrix.shard }} - E2E_TEST_SHARD_AMOUNT: 4 - run: | - cd packages/e2e-tests - yarn test:e2e + + - name: Build E2E app + working-directory: packages/e2e-tests/test-applications/${{ matrix.test-application }} + timeout-minutes: 5 + run: yarn ${{ matrix.build-command || 'test:build' }} + + - name: Run E2E test + working-directory: packages/e2e-tests/test-applications/${{ matrix.test-application }} + timeout-minutes: 5 + run: yarn test:assert job_required_tests: name: All required tests passed or skipped diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index 3bd402dce4cf..d70c95f033e0 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -17,47 +17,123 @@ permissions: issues: write jobs: - job_canary_test: - name: Canary Tests + job_e2e_prepare: + name: Prepare E2E Canary tests runs-on: ubuntu-20.04 - timeout-minutes: 60 + timeout-minutes: 30 steps: - - name: 'Check out current commit' + - name: Check out current commit uses: actions/checkout@v3 with: ref: ${{ env.HEAD_COMMIT }} - - uses: pnpm/action-setup@v2 - with: - version: 8.3.1 - name: Set up Node uses: actions/setup-node@v3 with: node-version-file: 'package.json' + - name: Check canary cache + uses: actions/cache@v3 + with: + path: | + ${{ github.workspace }}/packages/*/*.tgz + ${{ github.workspace }}/node_modules + ${{ github.workspace }}/packages/*/node_modules + key: canary-${{ env.HEAD_COMMIT }} - name: Install dependencies - run: yarn install --ignore-engines --frozen-lockfile + run: yarn install - name: Build packages run: yarn build + + - name: Build tarballs + run: yarn build:tarball + + job_e2e_tests: + name: E2E ${{ matrix.label }} Test + needs: [job_e2e_prepare] + runs-on: ubuntu-20.04 + timeout-minutes: 15 + env: + E2E_TEST_AUTH_TOKEN: ${{ secrets.E2E_TEST_AUTH_TOKEN }} + E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + # Needed because certain apps expect a certain prefix + NEXT_PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + PUBLIC_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + REACT_APP_E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} + E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' + E2E_TEST_SENTRY_TEST_PROJECT: 'sentry-javascript-e2e-tests' + strategy: + fail-fast: false + matrix: + include: + - test-application: 'create-react-app' + build-command: 'test:build-canary' + label: 'create-react-app (canary)' + - test-application: 'nextjs-app-dir' + build-command: 'test:build-canary' + label: 'nextjs-app-dir (canary)' + - test-application: 'nextjs-app-dir' + build-command: 'test:build-latest' + label: 'nextjs-app-dir (latest)' + - test-application: 'react-create-hash-router' + build-command: 'test:build-canary' + label: 'react-create-hash-router (canary)' + - test-application: 'standard-frontend-react' + build-command: 'test:build-canary' + label: 'standard-frontend-react (canary)' + + steps: + - name: Check out current commit + uses: actions/checkout@v3 + with: + ref: ${{ env.HEAD_COMMIT }} + - uses: pnpm/action-setup@v2 + with: + version: 8.3.1 + - name: Set up Node + uses: actions/setup-node@v3 + with: + node-version-file: 'package.json' + + - name: Restore canary cache + uses: actions/cache/restore@v3 + with: + path: | + ${{ github.workspace }}/packages/*/*.tgz + ${{ github.workspace }}/node_modules + ${{ github.workspace }}/packages/*/node_modules + key: canary-${{ env.HEAD_COMMIT }} + - name: Get node version id: versions run: | echo "echo node=$(jq -r '.volta.node' package.json)" >> $GITHUB_OUTPUT - - name: Run Canary Tests + + - name: Validate Verdaccio + run: yarn test:validate + working-directory: packages/e2e-tests + + - name: Prepare Verdaccio + run: yarn test:prepare + working-directory: packages/e2e-tests env: E2E_TEST_PUBLISH_SCRIPT_NODE_VERSION: ${{ steps.versions.outputs.node }} - E2E_TEST_AUTH_TOKEN: ${{ secrets.E2E_TEST_AUTH_TOKEN }} - E2E_TEST_DSN: ${{ secrets.E2E_TEST_DSN }} - E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' - E2E_TEST_SENTRY_TEST_PROJECT: 'sentry-javascript-e2e-tests' - CANARY_E2E_TEST: 'yes' - run: | - cd packages/e2e-tests - yarn test:e2e + + - name: Build E2E app + working-directory: packages/e2e-tests/test-applications/${{ matrix.test-application }} + timeout-minutes: 5 + run: yarn ${{ matrix.build-command }} + + - name: Run E2E test + working-directory: packages/e2e-tests/test-applications/${{ matrix.test-application }} + timeout-minutes: 5 + run: yarn test:assert + - name: Create Issue if: failure() && github.event_name == 'schedule' uses: JasonEtco/create-an-issue@e27dddc79c92bc6e4562f268fffa5ed752639abd env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RUN_LINK: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + TITLE: ${{ matrix.label }} Test Failed with: filename: .github/CANARY_FAILURE_TEMPLATE.md update_existing: true @@ -98,7 +174,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RUN_LINK: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + TITLE: Ember Canary ${{ matrix.scenario }} Test Failed with: filename: .github/CANARY_FAILURE_TEMPLATE.md update_existing: true - title: 'Ember Canary tests failed' diff --git a/.github/workflows/issue-package-label.yml b/.github/workflows/issue-package-label.yml new file mode 100644 index 000000000000..920c44a73610 --- /dev/null +++ b/.github/workflows/issue-package-label.yml @@ -0,0 +1,91 @@ +name: 'Tag issue with package label' + +on: + issues: + types: [opened] + +jobs: + add_labels: + name: Add package label + runs-on: ubuntu-latest + if: ${{ !github.event.issue.pull_request }} + steps: + - name: Get used package from issue body + # https://github.com/actions-ecosystem/action-regex-match + uses: actions-ecosystem/action-regex-match@v2 + id: packageName + with: + # Parse used package from issue body + text: ${{ github.event.issue.body }} + regex: '### Which SDK are you using\?\n\n(.*)\n\n' + + - name: Map package to issue label + # https://github.com/kanga333/variable-mapper + uses: kanga333/variable-mapper@v0.3.0 + id: packageLabel + if: steps.packageName.outputs.match != '' + with: + key: '${{ steps.packageName.outputs.group1 }}' + # Note: Since this is handled as a regex, and JSON parse wrangles slashes /, we just use `.` instead + map: | + { + "@sentry.browser": { + "label": "Package: Browser" + }, + "@sentry.angular": { + "label": "Package: Angular" + }, + "@sentry.angular-ivy": { + "label": "Package: Angular" + }, + "@sentry.ember": { + "label": "Package: ember" + }, + "@sentry.gatsby": { + "label": "Package: gatbsy" + }, + "@sentry.nextjs": { + "label": "Package: Nextjs" + }, + "@sentry.node": { + "label": "Package: Node" + }, + "@sentry.opentelemetry-node": { + "label": "Package: otel-node" + }, + "@sentry.react": { + "label": "Package: react" + }, + "@sentry.remix": { + "label": "Package: remix" + }, + "@sentry.serverless": { + "label": "Package: Serverless" + }, + "@sentry.svelte": { + "label": "Package: svelte" + }, + "@sentry.sveltekit": { + "label": "Package: SvelteKit" + }, + "@sentry.vue": { + "label": "Package: vue" + }, + "@sentry.wasm": { + "label": "Package: wasm" + }, + "Sentry.Browser.Loader": { + "label": "Package-Meta: Loader" + }, + "Sentry.Browser.CDN.bundle": { + "label": "Package-Meta: CDN" + } + } + export_to: output + + - name: Add package label if applicable + # Note: We only add the label if the issue is still open + if: steps.packageLabel.outputs.label != '' + uses: actions-ecosystem/action-add-labels@v1 + with: + labels: ${{ steps.packageLabel.outputs.label }} diff --git a/.size-limit.js b/.size-limit.js index 863b3628ebb1..417703ffd105 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -1,93 +1,105 @@ module.exports = [ + // Main browser webpack builds { - name: '@sentry/browser - ES5 CDN Bundle (gzipped + minified)', - path: 'packages/browser/build/bundles/bundle.es5.min.js', + name: '@sentry/browser (incl. Tracing, Replay) - Webpack (gzipped)', + path: 'packages/browser/build/npm/esm/index.js', + import: '{ init, Replay, BrowserTracing }', gzip: true, - limit: '30 KB', - }, - { - name: '@sentry/browser - ES5 CDN Bundle (minified)', - path: 'packages/browser/build/bundles/bundle.es5.min.js', - gzip: false, - limit: '70 KB', + limit: '80 KB', }, { - name: '@sentry/browser - ES6 CDN Bundle (gzipped + minified)', - path: 'packages/browser/build/bundles/bundle.min.js', + name: '@sentry/browser (incl. Tracing) - Webpack (gzipped)', + path: 'packages/browser/build/npm/esm/index.js', + import: '{ init, BrowserTracing }', gzip: true, - limit: '28 KB', - }, - { - name: '@sentry/browser - ES6 CDN Bundle (minified)', - path: 'packages/browser/build/bundles/bundle.min.js', - gzip: false, - limit: '65 KB', + limit: '35 KB', }, { - name: '@sentry/browser - Webpack (gzipped + minified)', + name: '@sentry/browser - Webpack (gzipped)', path: 'packages/browser/build/npm/esm/index.js', import: '{ init }', gzip: true, - limit: '30 KB', + limit: '28 KB', }, + + // Browser CDN bundles (ES6) { - name: '@sentry/browser - Webpack (minified)', - path: 'packages/browser/build/npm/esm/index.js', - import: '{ init }', - gzip: false, - limit: '76 KB', + name: '@sentry/browser (incl. Tracing, Replay) - ES6 CDN Bundle (gzipped)', + path: 'packages/browser/build/bundles/bundle.tracing.replay.min.js', + gzip: true, + limit: '80 KB', }, { - name: '@sentry/react - Webpack (gzipped + minified)', - path: 'packages/react/build/esm/index.js', - import: '{ init }', + name: '@sentry/browser (incl. Tracing) - ES6 CDN Bundle (gzipped)', + path: 'packages/browser/build/bundles/bundle.tracing.min.js', gzip: true, - limit: '30 KB', + limit: '35 KB', }, { - name: '@sentry/nextjs Client - Webpack (gzipped + minified)', - path: 'packages/nextjs/build/esm/client/index.js', - import: '{ init }', + name: '@sentry/browser - ES6 CDN Bundle (gzipped)', + path: 'packages/browser/build/bundles/bundle.min.js', gzip: true, - limit: '57 KB', + limit: '28 KB', }, + + // browser CDN bundles (ES6 + non-gzipped) { - name: '@sentry/browser + @sentry/tracing - ES5 CDN Bundle (gzipped + minified)', - path: 'packages/browser/build/bundles/bundle.tracing.es5.min.js', - gzip: true, - limit: '37 KB', + name: '@sentry/browser (incl. Tracing, Replay) - ES6 CDN Bundle (minified & uncompressed)', + path: 'packages/browser/build/bundles/bundle.tracing.replay.min.js', + gzip: false, + limit: '250 KB', }, { - name: '@sentry/browser + @sentry/tracing - ES6 CDN Bundle (gzipped + minified)', + name: '@sentry/browser (incl. Tracing) - ES6 CDN Bundle (minified & uncompressed)', path: 'packages/browser/build/bundles/bundle.tracing.min.js', + gzip: false, + limit: '100 KB', + }, + { + name: '@sentry/browser - ES6 CDN Bundle (minified & uncompressed)', + path: 'packages/browser/build/bundles/bundle.min.js', + gzip: false, + limit: '70 KB', + }, + + // Browser CDN bundles (ES5) + // Replay is not supported in ES5 mode + { + name: '@sentry/browser (incl. Tracing) - ES5 CDN Bundle (gzipped)', + path: 'packages/browser/build/bundles/bundle.tracing.es5.min.js', gzip: true, limit: '35 KB', }, + + // React { - name: '@sentry/replay ES6 CDN Bundle (gzipped + minified)', - path: 'packages/replay/build/bundles/replay.min.js', + name: '@sentry/react (incl. Tracing, Replay) - Webpack (gzipped)', + path: 'packages/react/build/esm/index.js', + import: '{ init, BrowserTYracing, Replay }', gzip: true, - limit: '52 KB', - ignore: ['@sentry/browser', '@sentry/utils', '@sentry/core', '@sentry/types'], + limit: '80 KB', }, { - name: '@sentry/replay - Webpack (gzipped + minified)', - path: 'packages/replay/build/npm/esm/index.js', - import: '{ Replay }', + name: '@sentry/react - Webpack (gzipped)', + path: 'packages/react/build/esm/index.js', + import: '{ init }', gzip: true, - limit: '48 KB', - ignore: ['@sentry/browser', '@sentry/utils', '@sentry/core', '@sentry/types'], + limit: '30 KB', }, + + // Next.js { - name: '@sentry/browser + @sentry/tracing + @sentry/replay - ES6 CDN Bundle (gzipped + minified)', - path: 'packages/browser/build/bundles/bundle.tracing.replay.min.js', + name: '@sentry/nextjs Client (incl. Tracing, Replay) - Webpack (gzipped)', + path: 'packages/nextjs/build/esm/client/index.js', + import: '{ init, BrowserTracing, Replay }', gzip: true, - limit: '80 KB', + limit: '100 KB', }, { - name: '@sentry/browser + @sentry/replay - ES6 CDN Bundle (gzipped + minified)', - path: 'packages/browser/build/bundles/bundle.replay.min.js', + name: '@sentry/nextjs Client - Webpack (gzipped)', + path: 'packages/nextjs/build/esm/client/index.js', + import: '{ init }', gzip: true, - limit: '80 KB', + limit: '57 KB', }, ]; diff --git a/CHANGELOG.md b/CHANGELOG.md index cc9f10fddf1e..ebf97364ee90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,12 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 7.60.1 + +- fix(nextjs): Match folder paths with trailing separator (#8615) +- fix(replay): Ignore clicks with `shift` pressed (#8648) +- fix(replay): Use `session.started` for min/max duration check (#8617) + ## 7.60.0 ### Important Changes diff --git a/packages/browser-integration-tests/README.md b/packages/browser-integration-tests/README.md index 075163a4b9dc..6024b98a8085 100644 --- a/packages/browser-integration-tests/README.md +++ b/packages/browser-integration-tests/README.md @@ -47,12 +47,12 @@ Tests can be run locally using the latest version of Chromium with: To run tests with a different browser such as `firefox` or `webkit`: -`yarn test --browser='firefox'` -`yarn test --browser='webkit'` +`yarn test --project='firefox'` +`yarn test --project='webkit'` Or to run on all three browsers: -`yarn test --browser='all'` +`yarn test:all` To filter tests by their title: diff --git a/packages/browser-integration-tests/package.json b/packages/browser-integration-tests/package.json index 29355db6c034..74ee2fe69778 100644 --- a/packages/browser-integration-tests/package.json +++ b/packages/browser-integration-tests/package.json @@ -18,7 +18,8 @@ "fix:prettier": "prettier --write \"{suites,utils}/**/*.ts\"", "type-check": "tsc", "pretest": "yarn clean && yarn type-check", - "test": "playwright test ./suites", + "test": "playwright test ./suites --project='chromium'", + "test:all": "playwright test ./suites", "test:bundle:es5": "PW_BUNDLE=bundle_es5 yarn test", "test:bundle:es5:min": "PW_BUNDLE=bundle_es5_min yarn test", "test:bundle:es6": "PW_BUNDLE=bundle_es6 yarn test", @@ -33,15 +34,15 @@ "test:bundle:tracing:replay:es6:min": "PW_BUNDLE=bundle_tracing_replay_es6_min yarn test", "test:cjs": "PW_BUNDLE=cjs yarn test", "test:esm": "PW_BUNDLE=esm yarn test", - "test:loader": "playwright test ./loader-suites", + "test:loader": "playwright test ./loader-suites --project='chromium'", "test:loader:base": "PW_BUNDLE=loader_base yarn test:loader", "test:loader:eager": "PW_BUNDLE=loader_eager yarn test:loader", "test:loader:tracing": "PW_BUNDLE=loader_tracing yarn test:loader", "test:loader:replay": "PW_BUNDLE=loader_replay yarn test:loader", "test:loader:full": "PW_BUNDLE=loader_tracing_replay yarn test:loader", "test:loader:debug": "PW_BUNDLE=loader_debug yarn test:loader", - "test:ci": "playwright test ./suites --browser='all' --reporter='line'", - "test:update-snapshots": "yarn test --update-snapshots --browser='all' && yarn test --update-snapshots", + "test:ci": "playwright test ./suites --reporter='line'", + "test:update-snapshots": "yarn test:all --update-snapshots", "test:detect-flaky": "ts-node scripts/detectFlakyTests.ts", "validate:es5": "es-check es5 'fixtures/loader.js'" }, diff --git a/packages/browser-integration-tests/playwright.config.ts b/packages/browser-integration-tests/playwright.config.ts index 8b85d4a83f4f..05d0ada178ae 100644 --- a/packages/browser-integration-tests/playwright.config.ts +++ b/packages/browser-integration-tests/playwright.config.ts @@ -1,4 +1,5 @@ import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; const config: PlaywrightTestConfig = { retries: 0, @@ -8,6 +9,22 @@ const config: PlaywrightTestConfig = { // Note that 3 is a random number selected to work well with our CI setup workers: process.env.CI ? 3 : undefined, + projects: [ + { + name: 'chromium', + use: devices['Desktop Chrome'], + }, + { + name: 'webkit', + use: devices['Desktop Safari'], + }, + { + name: 'firefox', + grep: /@firefox/i, + use: devices['Desktop Firefox'], + }, + ], + globalSetup: require.resolve('./playwright.setup.ts'), }; diff --git a/packages/browser-integration-tests/scripts/detectFlakyTests.ts b/packages/browser-integration-tests/scripts/detectFlakyTests.ts index 11eb04e651e4..12d83f30af5b 100644 --- a/packages/browser-integration-tests/scripts/detectFlakyTests.ts +++ b/packages/browser-integration-tests/scripts/detectFlakyTests.ts @@ -46,7 +46,7 @@ ${changedPaths.join('\n')} const cp = childProcess.spawn( `yarn playwright test ${ testPaths.length ? testPaths.join(' ') : './suites' - } --browser='all' --reporter='line' --repeat-each ${runCount}`, + } --reporter='line' --repeat-each ${runCount}`, { shell: true, cwd }, ); diff --git a/packages/browser-integration-tests/suites/public-api/captureException/errorEvent/test.ts b/packages/browser-integration-tests/suites/public-api/captureException/errorEvent/test.ts index dbcaaf24a1cf..9c09ba374e78 100644 --- a/packages/browser-integration-tests/suites/public-api/captureException/errorEvent/test.ts +++ b/packages/browser-integration-tests/suites/public-api/captureException/errorEvent/test.ts @@ -4,11 +4,7 @@ import type { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; import { getFirstSentryEnvelopeRequest } from '../../../../utils/helpers'; -sentryTest('should capture an ErrorEvent', async ({ getLocalTestPath, page, browserName }) => { - // On Firefox, the ErrorEvent has the `error` property and thus is handled separately - if (browserName === 'firefox') { - sentryTest.skip(); - } +sentryTest('should capture an ErrorEvent', async ({ getLocalTestPath, page }) => { const url = await getLocalTestPath({ testDir: __dirname }); const eventData = await getFirstSentryEnvelopeRequest(page, url); diff --git a/packages/browser-integration-tests/suites/replay/bufferMode/test.ts b/packages/browser-integration-tests/suites/replay/bufferMode/test.ts index 4785c1a5b158..5b9c0da33899 100644 --- a/packages/browser-integration-tests/suites/replay/bufferMode/test.ts +++ b/packages/browser-integration-tests/suites/replay/bufferMode/test.ts @@ -16,8 +16,8 @@ import { sentryTest( '[buffer-mode] manually start buffer mode and capture buffer', async ({ getLocalTestPath, page, browserName }) => { - // This was sometimes flaky on firefox/webkit, so skipping for now - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // This was sometimes flaky on webkit, so skipping for now + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } @@ -159,8 +159,8 @@ sentryTest( sentryTest( '[buffer-mode] manually start buffer mode and capture buffer, but do not continue as session', async ({ getLocalTestPath, page, browserName }) => { - // This was sometimes flaky on firefox/webkit, so skipping for now - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // This was sometimes flaky on webkit, so skipping for now + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } @@ -287,8 +287,7 @@ sentryTest( sentryTest( '[buffer-mode] can sample on each error event', async ({ getLocalTestPath, page, browserName, enableConsole }) => { - // This was sometimes flaky on firefox/webkit, so skipping for now - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/customEvents/test.ts b/packages/browser-integration-tests/suites/replay/customEvents/test.ts index 92bcd081ea8f..d8d6750f9dcb 100644 --- a/packages/browser-integration-tests/suites/replay/customEvents/test.ts +++ b/packages/browser-integration-tests/suites/replay/customEvents/test.ts @@ -81,8 +81,8 @@ sentryTest( sentryTest( 'replay recording should contain a click breadcrumb when a button is clicked', async ({ forceFlushReplay, getLocalTestPath, page, browserName }) => { - // TODO(replay): This is flakey on firefox and webkit where clicks are flakey - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // TODO(replay): This is flakey on webkit where clicks are flakey + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } @@ -177,8 +177,8 @@ sentryTest( sentryTest( 'replay recording should contain an "options" breadcrumb for Replay SDK configuration', async ({ forceFlushReplay, getLocalTestPath, page, browserName }) => { - // TODO(replay): This is flakey on firefox and webkit where clicks are flakey - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // TODO(replay): This is flakey on webkit where clicks are flakey + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts index aa452cdd9307..15e9ab60dc3c 100644 --- a/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts +++ b/packages/browser-integration-tests/suites/replay/errors/errorMode/test.ts @@ -18,8 +18,8 @@ import { sentryTest( '[error-mode] should start recording and switch to session mode once an error is thrown', async ({ getLocalTestPath, page, browserName }) => { - // This was sometimes flaky on firefox/webkit, so skipping for now - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // This was sometimes flaky on webkit, so skipping for now + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts b/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts index 9385e044a75c..603758c95409 100644 --- a/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts +++ b/packages/browser-integration-tests/suites/replay/errors/errorsInSession/test.ts @@ -13,8 +13,8 @@ import { sentryTest( '[session-mode] replay event should contain an error id of an error that occurred during session recording', async ({ getLocalTestPath, page, browserName, forceFlushReplay }) => { - // Skipping this in firefox/webkit because it is flakey there - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // Skipping this in webkit because it is flakey there + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } @@ -86,8 +86,8 @@ sentryTest( sentryTest( '[session-mode] replay event should not contain an error id of a dropped error while recording', async ({ getLocalTestPath, page, forceFlushReplay, browserName }) => { - // Skipping this in firefox/webkit because it is flakey there - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // Skipping this in webkit because it is flakey there + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/largeMutations/defaultOptions/test.ts b/packages/browser-integration-tests/suites/replay/largeMutations/defaultOptions/test.ts index c0d8e8234da8..b7d0e558b844 100644 --- a/packages/browser-integration-tests/suites/replay/largeMutations/defaultOptions/test.ts +++ b/packages/browser-integration-tests/suites/replay/largeMutations/defaultOptions/test.ts @@ -6,7 +6,7 @@ import { getReplayRecordingContent, shouldSkipReplayTest, waitForReplayRequest } sentryTest( 'handles large mutations with default options', async ({ getLocalTestPath, page, forceFlushReplay, browserName }) => { - if (shouldSkipReplayTest() || ['webkit', 'firefox'].includes(browserName)) { + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/largeMutations/mutationLimit/test.ts b/packages/browser-integration-tests/suites/replay/largeMutations/mutationLimit/test.ts index b826daafe6b4..eb144fa012ef 100644 --- a/packages/browser-integration-tests/suites/replay/largeMutations/mutationLimit/test.ts +++ b/packages/browser-integration-tests/suites/replay/largeMutations/mutationLimit/test.ts @@ -11,7 +11,7 @@ import { sentryTest( 'handles large mutations by stopping replay when `mutationLimit` configured', async ({ getLocalTestPath, page, forceFlushReplay, browserName }) => { - if (shouldSkipReplayTest() || ['webkit', 'firefox'].includes(browserName)) { + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/maxReplayDuration/init.js b/packages/browser-integration-tests/suites/replay/maxReplayDuration/init.js new file mode 100644 index 000000000000..e5dfd8115207 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/maxReplayDuration/init.js @@ -0,0 +1,23 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window.Replay = new Sentry.Replay({ + flushMinDelay: 200, + flushMaxDelay: 200, + minReplayDuration: 0, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 0, + replaysSessionSampleRate: 1.0, + replaysOnErrorSampleRate: 0.0, + + integrations: [window.Replay], +}); + +window.Replay._replay.timeouts = { + sessionIdlePause: 1000, // this is usually 5min, but we want to test this with shorter times + sessionIdleExpire: 2000, // this is usually 15min, but we want to test this with shorter times + maxSessionLife: 2000, // default: 60min +}; diff --git a/packages/browser-integration-tests/suites/replay/maxReplayDuration/template.html b/packages/browser-integration-tests/suites/replay/maxReplayDuration/template.html new file mode 100644 index 000000000000..7223a20f82ba --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/maxReplayDuration/template.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/packages/browser-integration-tests/suites/replay/maxReplayDuration/test.ts b/packages/browser-integration-tests/suites/replay/maxReplayDuration/test.ts new file mode 100644 index 000000000000..1e27aef149e2 --- /dev/null +++ b/packages/browser-integration-tests/suites/replay/maxReplayDuration/test.ts @@ -0,0 +1,60 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../utils/fixtures'; +import { getExpectedReplayEvent } from '../../../utils/replayEventTemplates'; +import { getReplayEvent, shouldSkipReplayTest, waitForReplayRequest } from '../../../utils/replayHelpers'; + +const SESSION_MAX_AGE = 2000; + +sentryTest('keeps track of max duration across reloads', async ({ getLocalTestPath, page }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + const reqPromise0 = waitForReplayRequest(page, 0); + const reqPromise1 = waitForReplayRequest(page, 1); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestPath({ testDir: __dirname }); + + await page.goto(url); + + await new Promise(resolve => setTimeout(resolve, SESSION_MAX_AGE / 2)); + + await page.reload(); + await page.click('#button1'); + + // After the second reload, we should have a new session (because we exceeded max age) + const reqPromise3 = waitForReplayRequest(page, 0); + + await new Promise(resolve => setTimeout(resolve, SESSION_MAX_AGE / 2 + 100)); + + void page.click('#button1'); + await page.evaluate(`Object.defineProperty(document, 'visibilityState', { + configurable: true, + get: function () { + return 'hidden'; + }, + }); + document.dispatchEvent(new Event('visibilitychange'));`); + + const replayEvent0 = getReplayEvent(await reqPromise0); + expect(replayEvent0).toEqual(getExpectedReplayEvent({})); + + const replayEvent1 = getReplayEvent(await reqPromise1); + expect(replayEvent1).toEqual( + getExpectedReplayEvent({ + segment_id: 1, + }), + ); + + const replayEvent3 = getReplayEvent(await reqPromise3); + expect(replayEvent3).toEqual(getExpectedReplayEvent({})); +}); diff --git a/packages/browser-integration-tests/suites/replay/privacyInput/test.ts b/packages/browser-integration-tests/suites/replay/privacyInput/test.ts index dc071b9bf487..71f6bd77ae67 100644 --- a/packages/browser-integration-tests/suites/replay/privacyInput/test.ts +++ b/packages/browser-integration-tests/suites/replay/privacyInput/test.ts @@ -19,8 +19,8 @@ function isInputMutation( sentryTest( 'should mask input initial value and its changes', async ({ browserName, forceFlushReplay, getLocalTestPath, page }) => { - // TODO(replay): This is flakey on firefox and webkit (~1%) where we do not always get the latest mutation. - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // TODO(replay): This is flakey on webkit (~1%) where we do not always get the latest mutation. + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } @@ -91,8 +91,8 @@ sentryTest( sentryTest( 'should mask textarea initial value and its changes', async ({ browserName, forceFlushReplay, getLocalTestPath, page }) => { - // TODO(replay): This is flakey on firefox and webkit (~1%) where we do not always get the latest mutation. - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // TODO(replay): This is flakey on webkit (~1%) where we do not always get the latest mutation. + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/privacyInputMaskAll/test.ts b/packages/browser-integration-tests/suites/replay/privacyInputMaskAll/test.ts index 9b4470118422..cbcf29f4ec6f 100644 --- a/packages/browser-integration-tests/suites/replay/privacyInputMaskAll/test.ts +++ b/packages/browser-integration-tests/suites/replay/privacyInputMaskAll/test.ts @@ -19,8 +19,8 @@ function isInputMutation( sentryTest( 'should mask input initial value and its changes from `maskAllInputs` and allow unmasked selector', async ({ browserName, forceFlushReplay, getLocalTestPath, page }) => { - // TODO(replay): This is flakey on firefox and webkit (~1%) where we do not always get the latest mutation. - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // TODO(replay): This is flakey on webkit (~1%) where we do not always get the latest mutation. + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } @@ -78,8 +78,8 @@ sentryTest( sentryTest( 'should mask textarea initial value and its changes from `maskAllInputs` and allow unmasked selector', async ({ browserName, forceFlushReplay, getLocalTestPath, page }) => { - // TODO(replay): This is flakey on firefox and webkit (~1%) where we do not always get the latest mutation. - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + // TODO(replay): This is flakey on webkit (~1%) where we do not always get the latest mutation. + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/requests/test.ts b/packages/browser-integration-tests/suites/replay/requests/test.ts index de879a920b0e..ba1d6ec5d0a2 100644 --- a/packages/browser-integration-tests/suites/replay/requests/test.ts +++ b/packages/browser-integration-tests/suites/replay/requests/test.ts @@ -5,10 +5,8 @@ import { expectedFetchPerformanceSpan, expectedXHRPerformanceSpan } from '../../ import { getReplayRecordingContent, shouldSkipReplayTest, waitForReplayRequest } from '../../../utils/replayHelpers'; sentryTest('replay recording should contain fetch request span', async ({ getLocalTestPath, page, browserName }) => { - // For some reason, observing and waiting for requests in firefox/webkit is extremely flaky. - // We therefore skip this test for firefox and only test on chromium. // Possibly related: https://github.com/microsoft/playwright/issues/11390 - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } @@ -48,9 +46,7 @@ sentryTest('replay recording should contain fetch request span', async ({ getLoc }); sentryTest('replay recording should contain XHR request span', async ({ getLocalTestPath, page, browserName }) => { - // For some reason, observing and waiting for requests in firefox/webkit is extremely flaky. - // We therefore skip this test for firefox and only test on chromium. - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts index bd303c9e68c3..f1765b2a3c22 100644 --- a/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts +++ b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts @@ -14,9 +14,8 @@ import { // Session should expire after 2s - keep in sync with init.js const SESSION_TIMEOUT = 2000; -sentryTest('handles an expired session', async ({ browserName, getLocalTestPath, page }) => { - // This test seems to only be flakey on firefox - if (shouldSkipReplayTest() || ['firefox'].includes(browserName)) { +sentryTest('handles an expired session', async ({ getLocalTestPath, page }) => { + if (shouldSkipReplayTest()) { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-firefox.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-firefox.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0-firefox.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-0.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-firefox.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-firefox.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2-firefox.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2.json b/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionExpiry/test.ts-snapshots/snapshot-2.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-firefox.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-firefox.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0-firefox.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-0.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-firefox.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-firefox.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1-firefox.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1.json b/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionInactive/test.ts-snapshots/snapshot-1.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-firefox.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-firefox.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0-firefox.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-0.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-firefox.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-firefox.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2-firefox.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2.json b/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2.json deleted file mode 100644 index d510b410a343..000000000000 --- a/packages/browser-integration-tests/suites/replay/sessionMaxAge/test.ts-snapshots/snapshot-2.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "type": 2, - "data": { - "node": { - "type": 0, - "childNodes": [ - { - "type": 1, - "name": "html", - "publicId": "", - "systemId": "", - "id": 2 - }, - { - "type": 2, - "tagName": "html", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "head", - "attributes": {}, - "childNodes": [ - { - "type": 2, - "tagName": "meta", - "attributes": { - "charset": "utf-8" - }, - "childNodes": [], - "id": 5 - } - ], - "id": 4 - }, - { - "type": 3, - "textContent": "\n ", - "id": 6 - }, - { - "type": 2, - "tagName": "body", - "attributes": {}, - "childNodes": [ - { - "type": 3, - "textContent": "\n ", - "id": 8 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 1')", - "id": "button1" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 10 - } - ], - "id": 9 - }, - { - "type": 3, - "textContent": "\n ", - "id": 11 - }, - { - "type": 2, - "tagName": "button", - "attributes": { - "onclick": "console.log('Test log 2')", - "id": "button2" - }, - "childNodes": [ - { - "type": 3, - "textContent": "***** **", - "id": 13 - } - ], - "id": 12 - }, - { - "type": 3, - "textContent": "\n ", - "id": 14 - }, - { - "type": 3, - "textContent": "\n\n", - "id": 15 - } - ], - "id": 7 - } - ], - "id": 3 - } - ], - "id": 1 - }, - "initialOffset": { - "left": 0, - "top": 0 - } - }, - "timestamp": [timestamp] -} \ No newline at end of file diff --git a/packages/browser-integration-tests/suites/replay/slowClick/init.js b/packages/browser-integration-tests/suites/replay/slowClick/init.js index 030b2722d236..575e490b2927 100644 --- a/packages/browser-integration-tests/suites/replay/slowClick/init.js +++ b/packages/browser-integration-tests/suites/replay/slowClick/init.js @@ -5,7 +5,7 @@ window.Replay = new Sentry.Replay({ flushMinDelay: 200, flushMaxDelay: 200, minReplayDuration: 0, - slowClickTimeout: 3100, + slowClickTimeout: 3500, slowClickIgnoreSelectors: ['.ignore-class', '[ignore-attribute]'], }); diff --git a/packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts b/packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts index 370db54777f3..cdb099d82a18 100644 --- a/packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts +++ b/packages/browser-integration-tests/suites/replay/slowClick/multiClick/test.ts @@ -102,8 +102,7 @@ sentryTest('captures multi click when not detecting slow click', async ({ getLoc }); sentryTest('captures multiple multi clicks', async ({ getLocalTestUrl, page, forceFlushReplay, browserName }) => { - // This test seems to only be flakey on firefox and webkit - if (shouldSkipReplayTest() || ['firefox', 'webkit'].includes(browserName)) { + if (shouldSkipReplayTest() || browserName === 'webkit') { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/slowClick/mutation/test.ts b/packages/browser-integration-tests/suites/replay/slowClick/mutation/test.ts index bab50e12938c..cb5b9160d13b 100644 --- a/packages/browser-integration-tests/suites/replay/slowClick/mutation/test.ts +++ b/packages/browser-integration-tests/suites/replay/slowClick/mutation/test.ts @@ -61,7 +61,7 @@ sentryTest('mutation after threshold results in slow click', async ({ forceFlush ]); expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeGreaterThan(3000); - expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeLessThan(3100); + expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeLessThan(3500); }); sentryTest('multiple clicks are counted', async ({ getLocalTestUrl, page }) => { @@ -123,64 +123,60 @@ sentryTest('multiple clicks are counted', async ({ getLocalTestUrl, page }) => { expect(multiClickBreadcrumbs.length).toEqual(0); expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeGreaterThan(3000); - expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeLessThan(3100); + expect(slowClickBreadcrumbs[0]?.data?.timeAfterClickMs).toBeLessThan(3500); }); -sentryTest( - 'immediate mutation does not trigger slow click', - async ({ forceFlushReplay, browserName, getLocalTestUrl, page }) => { - // This test seems to only be flakey on firefox - if (shouldSkipReplayTest() || ['firefox'].includes(browserName)) { - sentryTest.skip(); - } - - const reqPromise0 = waitForReplayRequest(page, 0); - - await page.route('https://dsn.ingest.sentry.io/**/*', route => { - return route.fulfill({ - status: 200, - contentType: 'application/json', - body: JSON.stringify({ id: 'test-id' }), - }); +sentryTest('immediate mutation does not trigger slow click', async ({ forceFlushReplay, getLocalTestUrl, page }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + const reqPromise0 = waitForReplayRequest(page, 0); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), }); + }); - const url = await getLocalTestUrl({ testDir: __dirname }); + const url = await getLocalTestUrl({ testDir: __dirname }); - await page.goto(url); - await forceFlushReplay(); - await reqPromise0; + await page.goto(url); + await forceFlushReplay(); + await reqPromise0; - const reqPromise1 = waitForReplayRequest(page, (event, res) => { - const { breadcrumbs } = getCustomRecordingEvents(res); + const reqPromise1 = waitForReplayRequest(page, (event, res) => { + const { breadcrumbs } = getCustomRecordingEvents(res); - return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.click'); - }); + return breadcrumbs.some(breadcrumb => breadcrumb.category === 'ui.click'); + }); + + await page.click('#mutationButtonImmediately'); - await page.click('#mutationButtonImmediately'); - - const { breadcrumbs } = getCustomRecordingEvents(await reqPromise1); - - expect(breadcrumbs).toEqual([ - { - category: 'ui.click', - data: { - node: { - attributes: { - id: 'mutationButtonImmediately', - }, - id: expect.any(Number), - tagName: 'button', - textContent: '******* ******** ***********', + const { breadcrumbs } = getCustomRecordingEvents(await reqPromise1); + + expect(breadcrumbs).toEqual([ + { + category: 'ui.click', + data: { + node: { + attributes: { + id: 'mutationButtonImmediately', }, - nodeId: expect.any(Number), + id: expect.any(Number), + tagName: 'button', + textContent: '******* ******** ***********', }, - message: 'body > button#mutationButtonImmediately', - timestamp: expect.any(Number), - type: 'default', + nodeId: expect.any(Number), }, - ]); - }, -); + message: 'body > button#mutationButtonImmediately', + timestamp: expect.any(Number), + type: 'default', + }, + ]); +}); sentryTest('inline click handler does not trigger slow click', async ({ forceFlushReplay, getLocalTestUrl, page }) => { if (shouldSkipReplayTest()) { @@ -234,9 +230,8 @@ sentryTest('inline click handler does not trigger slow click', async ({ forceFlu ]); }); -sentryTest('mouseDown events are considered', async ({ browserName, getLocalTestUrl, page }) => { - // This test seems to only be flakey on firefox - if (shouldSkipReplayTest() || ['firefox'].includes(browserName)) { +sentryTest('mouseDown events are considered', async ({ getLocalTestUrl, page }) => { + if (shouldSkipReplayTest()) { sentryTest.skip(); } diff --git a/packages/browser-integration-tests/suites/replay/slowClick/template.html b/packages/browser-integration-tests/suites/replay/slowClick/template.html index 19bd283db90e..45cc78aef02e 100644 --- a/packages/browser-integration-tests/suites/replay/slowClick/template.html +++ b/packages/browser-integration-tests/suites/replay/slowClick/template.html @@ -53,7 +53,7 @@

Bottom

document.getElementById('mutationButtonLate').addEventListener('click', () => { setTimeout(() => { document.getElementById('out').innerHTML += 'mutationButtonLate clicked
'; - }, 3101); + }, 3501); }); document.getElementById('mutationButtonImmediately').addEventListener('click', () => { document.getElementById('out').innerHTML += 'mutationButtonImmediately clicked
'; diff --git a/packages/browser-integration-tests/suites/replay/slowClick/timeout/test.ts b/packages/browser-integration-tests/suites/replay/slowClick/timeout/test.ts index 7e94e0b68f15..ed53bc2d5fb4 100644 --- a/packages/browser-integration-tests/suites/replay/slowClick/timeout/test.ts +++ b/packages/browser-integration-tests/suites/replay/slowClick/timeout/test.ts @@ -51,7 +51,7 @@ sentryTest('mutation after timeout results in slow click', async ({ getLocalTest textContent: '******* ******** ****', }, nodeId: expect.any(Number), - timeAfterClickMs: 3100, + timeAfterClickMs: 3500, url: 'http://sentry-test.io/index.html', }, message: 'body > button#mutationButtonLate', @@ -108,7 +108,7 @@ sentryTest('console.log results in slow click', async ({ getLocalTestUrl, page } textContent: '******* ******* ***', }, nodeId: expect.any(Number), - timeAfterClickMs: 3100, + timeAfterClickMs: 3500, url: 'http://sentry-test.io/index.html', }, message: 'body > button#consoleLogButton', diff --git a/packages/browser-integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/test.ts b/packages/browser-integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/test.ts index cd79799df328..4650c6b94f3c 100644 --- a/packages/browser-integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/test.ts +++ b/packages/browser-integration-tests/suites/stacktraces/protocol_containing_fn_identifiers/test.ts @@ -5,7 +5,7 @@ import { sentryTest } from '../../../utils/fixtures'; import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; sentryTest( - 'should parse function identifiers that contain protocol names correctly', + 'should parse function identifiers that contain protocol names correctly @firefox', async ({ getLocalTestPath, page, runInChromium, runInFirefox, runInWebkit }) => { const url = await getLocalTestPath({ testDir: __dirname }); diff --git a/packages/browser-integration-tests/suites/stacktraces/protocol_fn_identifiers/test.ts b/packages/browser-integration-tests/suites/stacktraces/protocol_fn_identifiers/test.ts index c4ef75913334..c0c813058128 100644 --- a/packages/browser-integration-tests/suites/stacktraces/protocol_fn_identifiers/test.ts +++ b/packages/browser-integration-tests/suites/stacktraces/protocol_fn_identifiers/test.ts @@ -5,7 +5,7 @@ import { sentryTest } from '../../../utils/fixtures'; import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; sentryTest( - 'should parse function identifiers that are protocol names correctly', + 'should parse function identifiers that are protocol names correctly @firefox', async ({ getLocalTestPath, page, runInChromium, runInFirefox, runInWebkit }) => { const url = await getLocalTestPath({ testDir: __dirname }); diff --git a/packages/browser-integration-tests/suites/stacktraces/regular_fn_identifiers/test.ts b/packages/browser-integration-tests/suites/stacktraces/regular_fn_identifiers/test.ts index e7a9f452fe6a..66f8286a4b5e 100644 --- a/packages/browser-integration-tests/suites/stacktraces/regular_fn_identifiers/test.ts +++ b/packages/browser-integration-tests/suites/stacktraces/regular_fn_identifiers/test.ts @@ -5,7 +5,7 @@ import { sentryTest } from '../../../utils/fixtures'; import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; sentryTest( - 'should parse function identifiers correctly', + 'should parse function identifiers correctly @firefox', async ({ getLocalTestPath, page, runInChromium, runInFirefox, runInWebkit }) => { const url = await getLocalTestPath({ testDir: __dirname }); diff --git a/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts index 17f5920c57de..485d50d12641 100644 --- a/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts +++ b/packages/browser-integration-tests/suites/tracing/browsertracing/backgroundtab-pageload/test.ts @@ -4,24 +4,20 @@ import type { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; import { getFirstSentryEnvelopeRequest, shouldSkipTracingTest } from '../../../../utils/helpers'; -sentryTest( - 'should finish pageload transaction when the page goes background', - async ({ browserName, getLocalTestPath, page }) => { - // TODO: This is flakey on firefox... trace.status is sometimes undefined - if (shouldSkipTracingTest() || ['firefox'].includes(browserName)) { - sentryTest.skip(); - } - const url = await getLocalTestPath({ testDir: __dirname }); +sentryTest('should finish pageload transaction when the page goes background', async ({ getLocalTestPath, page }) => { + if (shouldSkipTracingTest()) { + sentryTest.skip(); + } + const url = await getLocalTestPath({ testDir: __dirname }); - await page.goto(url); - await page.click('#go-background'); + await page.goto(url); + await page.click('#go-background'); - const pageloadTransaction = await getFirstSentryEnvelopeRequest(page); + const pageloadTransaction = await getFirstSentryEnvelopeRequest(page); - expect(pageloadTransaction.contexts?.trace?.op).toBe('pageload'); - expect(pageloadTransaction.contexts?.trace?.status).toBe('cancelled'); - expect(pageloadTransaction.contexts?.trace?.tags).toMatchObject({ - visibilitychange: 'document.hidden', - }); - }, -); + expect(pageloadTransaction.contexts?.trace?.op).toBe('pageload'); + expect(pageloadTransaction.contexts?.trace?.status).toBe('cancelled'); + expect(pageloadTransaction.contexts?.trace?.tags).toMatchObject({ + visibilitychange: 'document.hidden', + }); +}); diff --git a/packages/browser-integration-tests/suites/tracing/browsertracing/http-timings/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/http-timings/test.ts index 566c14297897..a8e7f9eec335 100644 --- a/packages/browser-integration-tests/suites/tracing/browsertracing/http-timings/test.ts +++ b/packages/browser-integration-tests/suites/tracing/browsertracing/http-timings/test.ts @@ -4,7 +4,7 @@ import type { Event } from '@sentry/types'; import { sentryTest } from '../../../../utils/fixtures'; import { getMultipleSentryEnvelopeRequests, shouldSkipTracingTest } from '../../../../utils/helpers'; -sentryTest('should create fetch spans with http timing', async ({ browserName, getLocalTestPath, page }) => { +sentryTest('should create fetch spans with http timing @firefox', async ({ browserName, getLocalTestPath, page }) => { const supportedBrowsers = ['chromium', 'firefox']; if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { diff --git a/packages/browser-integration-tests/suites/tracing/browsertracing/interactions/test.ts b/packages/browser-integration-tests/suites/tracing/browsertracing/interactions/test.ts index 9b161699b9c0..e79b724ec91a 100644 --- a/packages/browser-integration-tests/suites/tracing/browsertracing/interactions/test.ts +++ b/packages/browser-integration-tests/suites/tracing/browsertracing/interactions/test.ts @@ -18,7 +18,7 @@ type TransactionJSON = ReturnType & { const wait = (time: number) => new Promise(res => setTimeout(res, time)); -sentryTest('should capture interaction transaction.', async ({ browserName, getLocalTestPath, page }) => { +sentryTest('should capture interaction transaction. @firefox', async ({ browserName, getLocalTestPath, page }) => { const supportedBrowsers = ['chromium', 'firefox']; if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { @@ -55,23 +55,28 @@ sentryTest('should capture interaction transaction.', async ({ browserName, getL expect(interactionSpanDuration).toBeLessThan(200); }); -sentryTest('should create only one transaction per interaction', async ({ browserName, getLocalTestPath, page }) => { - const supportedBrowsers = ['chromium', 'firefox']; - - if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { - sentryTest.skip(); - } - - await page.route('**/path/to/script.js', (route: Route) => route.fulfill({ path: `${__dirname}/assets/script.js` })); - - const url = await getLocalTestPath({ testDir: __dirname }); - await page.goto(url); - await getFirstSentryEnvelopeRequest(page); - - for (let i = 0; i < 4; i++) { - await wait(100); - await page.locator('[data-test-id=interaction-button]').click(); - const envelope = await getMultipleSentryEnvelopeRequests(page, 1); - expect(envelope[0].spans).toHaveLength(1); - } -}); +sentryTest( + 'should create only one transaction per interaction @firefox', + async ({ browserName, getLocalTestPath, page }) => { + const supportedBrowsers = ['chromium', 'firefox']; + + if (shouldSkipTracingTest() || !supportedBrowsers.includes(browserName)) { + sentryTest.skip(); + } + + await page.route('**/path/to/script.js', (route: Route) => + route.fulfill({ path: `${__dirname}/assets/script.js` }), + ); + + const url = await getLocalTestPath({ testDir: __dirname }); + await page.goto(url); + await getFirstSentryEnvelopeRequest(page); + + for (let i = 0; i < 4; i++) { + await wait(100); + await page.locator('[data-test-id=interaction-button]').click(); + const envelope = await getMultipleSentryEnvelopeRequests(page, 1); + expect(envelope[0].spans).toHaveLength(1); + } + }, +); diff --git a/packages/browser-integration-tests/utils/generatePlugin.ts b/packages/browser-integration-tests/utils/generatePlugin.ts index c25cf823c4a5..45fe1cfb9a94 100644 --- a/packages/browser-integration-tests/utils/generatePlugin.ts +++ b/packages/browser-integration-tests/utils/generatePlugin.ts @@ -8,6 +8,7 @@ import { addStaticAsset, addStaticAssetSymlink } from './staticAssets'; const LOADER_TEMPLATE = fs.readFileSync(path.join(__dirname, '../fixtures/loader.js'), 'utf-8'); const PACKAGES_DIR = '../../packages'; +const PACKAGE_JSON = '../../package.json'; /** * Possible values: See BUNDLE_PATHS.browser @@ -100,11 +101,8 @@ export const LOADER_CONFIGS: Record; * so that the compiled versions aren't included */ function generateSentryAlias(): Record { - const packageNames = fs - .readdirSync(PACKAGES_DIR, { withFileTypes: true }) - .filter(dirent => dirent.isDirectory()) - .filter(dir => !['apm', 'minimal', 'next-plugin-sentry'].includes(dir.name)) - .map(dir => dir.name); + const rootPackageJson = JSON.parse(fs.readFileSync(PACKAGE_JSON, 'utf8')) as { workspaces: string[] }; + const packageNames = rootPackageJson.workspaces.map(workspace => workspace.replace('packages/', '')); return Object.fromEntries( packageNames.map(packageName => { diff --git a/packages/e2e-tests/.gitignore b/packages/e2e-tests/.gitignore index a16add542a3f..385b720a0ca2 100644 --- a/packages/e2e-tests/.gitignore +++ b/packages/e2e-tests/.gitignore @@ -1,2 +1,4 @@ .env tmp +.tmp_build_output +pnpm-lock.yaml diff --git a/packages/e2e-tests/README.md b/packages/e2e-tests/README.md index 69e4dc7a062a..cdb4a0e5ae04 100644 --- a/packages/e2e-tests/README.md +++ b/packages/e2e-tests/README.md @@ -15,6 +15,12 @@ Prerequisites: Docker yarn test:e2e ``` +Or run only a single E2E test app: + +```bash +yarn test:run +``` + ## How they work Before running any tests we launch a fake test registry (in our case [Verdaccio](https://verdaccio.org/docs/e2e/)), we @@ -43,40 +49,14 @@ cat > test-applications/my-new-test-application/.npmrc << EOF @sentry:registry=http://localhost:4873 @sentry-internal:registry=http://localhost:4873 EOF - -# Add a test recipe file to the test application -touch test-applications/my-new-test-application/test-recipe.json -``` - -To get you started with the recipe, you can copy the following into `test-recipe.json`: - -```json -{ - "$schema": "../../test-recipe-schema.json", - "testApplicationName": "My New Test Application", - "buildCommand": "pnpm install", - "tests": [ - { - "testName": "My new test", - "testCommand": "pnpm test", - "timeoutSeconds": 60 - } - ] -} ``` -The `test-recipe.json` files follow a schema (`e2e-tests/test-recipe-schema.json`). Here is a basic explanation of the -fields: +Make sure to add a `test:build` and `test:assert` command to the new app's `package.json` file. -- The `buildCommand` command runs only once before any of the tests and is supposed to build the test application. If - this command returns a non-zero exit code, it counts as a failed test and the test application's tests are not run. In - the example above, we use the `--pure-lockfile` flag to install dependencies without modifiying the lockfile so that - there aren't any changes in the git worktree after running the tests. -- The `testCommand` command is supposed to run tests on the test application. If the configured command returns a - non-zero exit code, it counts as a failed test. -- A test timeout can be configured via `timeoutSeconds`, it defaults to `60`. +Add the new test app to `test-application` matrix in `.github/workflows/build.yml` for the `job_e2e_tests` job. If you +want to run a canary test, add it to the `canary.yml` workflow. -**An important thing to note:** In the context of the `buildCommand` the fake test registry is available at +**An important thing to note:** In the context of the build/test commands the fake test registry is available at `http://localhost:4873`. It hosts all of our packages as if they were to be published with the state of the current branch. This means we can install the packages from this registry via the `.npmrc` configuration as seen above. If you add Sentry dependencies to your test application, you should set the dependency versions set to `latest || *` in order @@ -89,7 +69,9 @@ for it to work with both regular and prerelease versions: "version": "1.0.0", "private": true, "scripts": { - "test": "echo \"Hello world!\"" + "test": "echo \"Hello world!\"", + "test:build": "pnpm install", + "test:assert": "pnpm test" }, "dependencies": { "@sentry/node": "latest || *" diff --git a/packages/e2e-tests/lib/buildApp.ts b/packages/e2e-tests/lib/buildApp.ts deleted file mode 100644 index 426038f55b4c..000000000000 --- a/packages/e2e-tests/lib/buildApp.ts +++ /dev/null @@ -1,88 +0,0 @@ -/* eslint-disable no-console */ - -import * as fs from 'fs-extra'; -import * as path from 'path'; - -import { DEFAULT_BUILD_TIMEOUT_SECONDS } from './constants'; -import type { Env, RecipeInstance } from './types'; -import { prefixObjectKeys, spawnAsync } from './utils'; - -export async function buildApp(appDir: string, recipeInstance: RecipeInstance, envVars: Env): Promise { - const { recipe, label, dependencyOverrides } = recipeInstance; - - const packageJsonPath = path.resolve(appDir, 'package.json'); - - if (dependencyOverrides) { - // Override dependencies - const packageJson: { dependencies?: Record } = JSON.parse( - fs.readFileSync(packageJsonPath, { encoding: 'utf-8' }), - ); - packageJson.dependencies = packageJson.dependencies - ? { ...packageJson.dependencies, ...dependencyOverrides } - : dependencyOverrides; - fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2), { - encoding: 'utf-8', - }); - } - - if (recipe.buildCommand) { - console.log(`Running build command for test application "${label}"`); - - const env = { - ...process.env, - ...envVars, - }; - - const buildResult = await spawnAsync(recipe.buildCommand, { - cwd: appDir, - timeout: (recipe.buildTimeoutSeconds ?? DEFAULT_BUILD_TIMEOUT_SECONDS) * 1000, - env: { - ...env, - ...prefixObjectKeys(env, 'NEXT_PUBLIC_'), - ...prefixObjectKeys(env, 'REACT_APP_'), - ...prefixObjectKeys(env, 'PUBLIC_'), - }, - }); - - if (buildResult.error) { - console.log(`Build failed for test application "${label}"`); - - // Prepends some text to the output build command's output so we can distinguish it from logging in this script - console.log(buildResult.stdout.replace(/^/gm, ' [BUILD OUTPUT] ')); - console.log(buildResult.stderr.replace(/^/gm, ' [BUILD OUTPUT] ')); - - console.log('[BUILD ERROR] ', buildResult.error); - throw buildResult.error; - } - - if (recipe.buildAssertionCommand) { - console.log(`Running build assertion for test application "${label}"`); - - const buildAssertionResult = await spawnAsync( - recipe.buildAssertionCommand, - { - cwd: appDir, - timeout: (recipe.buildTimeoutSeconds ?? DEFAULT_BUILD_TIMEOUT_SECONDS) * 1000, - env: { - ...env, - ...prefixObjectKeys(env, 'NEXT_PUBLIC_'), - ...prefixObjectKeys(env, 'REACT_APP_'), - }, - }, - buildResult.stdout, - ); - - if (buildAssertionResult.error) { - console.log(`Build assertion failed for test application "${label}"`); - - // Prepends some text to the output build command's output so we can distinguish it from logging in this script - console.log(buildAssertionResult.stdout.replace(/^/gm, ' [BUILD ASSERTION OUTPUT] ')); - console.log(buildAssertionResult.stderr.replace(/^/gm, ' [BUILD ASSERTION OUTPUT] ')); - - console.log('[BUILD ASSERTION ERROR] ', buildAssertionResult.error); - - throw buildAssertionResult.error; - } - } - } -} diff --git a/packages/e2e-tests/lib/constructRecipeInstances.ts b/packages/e2e-tests/lib/constructRecipeInstances.ts deleted file mode 100644 index 86dfd1ef89f2..000000000000 --- a/packages/e2e-tests/lib/constructRecipeInstances.ts +++ /dev/null @@ -1,52 +0,0 @@ -import * as fs from 'fs'; - -import type { Recipe, RecipeInput, RecipeInstance } from './types'; - -export function constructRecipeInstances(recipePaths: string[]): RecipeInstance[] { - const recipes = buildRecipes(recipePaths); - const recipeInstances: Omit[] = []; - - recipes.forEach(recipe => { - recipe.versions.forEach(version => { - const dependencyOverrides = - Object.keys(version.dependencyOverrides).length > 0 ? version.dependencyOverrides : undefined; - const dependencyOverridesInformationString = dependencyOverrides - ? ` (Dependency overrides: ${JSON.stringify(dependencyOverrides)})` - : ''; - - recipeInstances.push({ - label: `${recipe.testApplicationName}${dependencyOverridesInformationString}`, - recipe, - dependencyOverrides, - }); - }); - }); - - return recipeInstances - .map((instance, i) => ({ ...instance, portModulo: i, portGap: recipeInstances.length })) - .filter((_, i) => { - if (process.env.E2E_TEST_SHARD && process.env.E2E_TEST_SHARD_AMOUNT) { - return (i + Number(process.env.E2E_TEST_SHARD)) % Number(process.env.E2E_TEST_SHARD_AMOUNT) === 0; - } else { - return true; - } - }); -} - -function buildRecipes(recipePaths: string[]): Recipe[] { - return recipePaths.map(recipePath => buildRecipe(recipePath)); -} - -function buildRecipe(recipePath: string): Recipe { - const recipe: RecipeInput = JSON.parse(fs.readFileSync(recipePath, 'utf-8')); - - const versions = process.env.CANARY_E2E_TEST - ? recipe.canaryVersions ?? [] - : recipe.versions ?? [{ dependencyOverrides: {} }]; - - return { - ...recipe, - path: recipePath, - versions, - }; -} diff --git a/packages/e2e-tests/lib/runAllTestApps.ts b/packages/e2e-tests/lib/runAllTestApps.ts deleted file mode 100644 index d5f4c247f2e6..000000000000 --- a/packages/e2e-tests/lib/runAllTestApps.ts +++ /dev/null @@ -1,89 +0,0 @@ -/* eslint-disable no-console */ - -import { constructRecipeInstances } from './constructRecipeInstances'; -import { buildAndTestApp } from './runTestApp'; -import type { RecipeInstance, RecipeTestResult } from './types'; - -export async function runAllTestApps( - recipePaths: string[], - envVarsToInject: Record, -): Promise { - const maxParallel = process.env.CANARY_E2E_TEST - ? 1 // TODO: figure out why concurrent tests fail for Next.js and remove this concurrency limitation - : process.env.CI - ? 3 - : 6; - - const recipeInstances = constructRecipeInstances(recipePaths); - - const results = await shardPromises( - recipeInstances, - recipeInstance => buildAndTestApp(recipeInstance, envVarsToInject), - maxParallel, - ); - - console.log('--------------------------------------'); - console.log('Test Result Summary:'); - - results.forEach(result => { - if (result.buildFailed) { - console.log(`● BUILD FAILED - ${result.label} (${result.recipe.path}`); - } else { - console.log(`● BUILD SUCCEEDED - ${result.label}`); - result.tests.forEach(testResult => { - console.log(` ● ${testResult.result.padEnd(7, ' ')} ${testResult.testName}`); - }); - } - }); - - const failed = results.filter(result => result.buildFailed || result.testFailed); - - if (failed.length) { - console.log(`${failed.length} test(s) failed.`); - process.exit(1); - } - - console.log('All tests succeeded. 🎉'); -} - -// Always run X promises at a time -function shardPromises( - recipes: RecipeInstance[], - callback: (recipe: RecipeInstance) => Promise, - maxParallel: number, -): Promise { - return new Promise(resolve => { - console.log(`Running a total of ${recipes.length} jobs, with up to ${maxParallel} jobs in parallel...`); - const results: RecipeTestResult[] = []; - const remaining = recipes.slice(); - const running: Promise[] = []; - - function runNext(): void { - if (running.length < maxParallel && remaining.length > 0) { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const next = remaining.shift()!; - const promise = callback(next); - - console.log(`Running job ${next.label}, ${remaining.length} remaining...`); - - running.push(promise); - - promise - .then(result => results.push(result)) - .finally(() => { - const pos = running.indexOf(promise); - running.splice(pos, 1); - - runNext(); - }); - } else if (remaining.length === 0 && running.length === 0) { - resolve(results); - } - } - - // Initial runs - for (let i = 0; i < maxParallel; i++) { - runNext(); - } - }); -} diff --git a/packages/e2e-tests/lib/runTestApp.ts b/packages/e2e-tests/lib/runTestApp.ts deleted file mode 100644 index 0d58757d3e35..000000000000 --- a/packages/e2e-tests/lib/runTestApp.ts +++ /dev/null @@ -1,58 +0,0 @@ -import * as fs from 'fs'; -import * as fsExtra from 'fs-extra'; -import * as path from 'path'; - -import { buildApp } from './buildApp'; -import { testApp } from './testApp'; -import type { Env, RecipeInstance, RecipeTestResult } from './types'; - -// This should never throw, we always return a result here -export async function buildAndTestApp( - recipeInstance: RecipeInstance, - envVarsToInject: Record, -): Promise { - const { recipe, portModulo, portGap } = recipeInstance; - const recipeDirname = path.dirname(recipe.path); - - const tmpFolder = path.join(__dirname, '..', 'tmp'); - await fs.promises.mkdir(tmpFolder, { recursive: true }); - const targetDir = await fs.promises.mkdtemp( - path.join(tmpFolder, `${recipeInstance.recipe.testApplicationName}-${Date.now()}-`), - ); - - await fsExtra.copy(recipeDirname, targetDir); - - const env: Env = { - ...envVarsToInject, - PORT_MODULO: portModulo.toString(), - PORT_GAP: portGap.toString(), - }; - - try { - await buildApp(targetDir, recipeInstance, env); - } catch (error) { - await fsExtra.remove(targetDir); - - return { - ...recipeInstance, - buildFailed: true, - testFailed: false, - tests: [], - }; - } - - // This cannot throw, we always return a result here - return testApp(targetDir, recipeInstance, env) - .finally(() => { - // Cleanup - void fsExtra.remove(targetDir); - }) - .then(results => { - return { - ...recipeInstance, - buildFailed: false, - testFailed: results.some(result => result.result !== 'PASS'), - tests: results, - }; - }); -} diff --git a/packages/e2e-tests/lib/testApp.ts b/packages/e2e-tests/lib/testApp.ts deleted file mode 100644 index 02743c26f633..000000000000 --- a/packages/e2e-tests/lib/testApp.ts +++ /dev/null @@ -1,61 +0,0 @@ -/* eslint-disable no-console */ - -import { DEFAULT_TEST_TIMEOUT_SECONDS } from './constants'; -import type { Env, RecipeInstance, TestDef, TestResult } from './types'; -import { prefixObjectKeys, spawnAsync } from './utils'; - -export async function testApp(appDir: string, recipeInstance: RecipeInstance, env: Env): Promise { - const { recipe } = recipeInstance; - - const results: TestResult[] = []; - for (const test of recipe.tests) { - results.push(await runTest(appDir, recipeInstance, test, env)); - } - - return results; -} - -async function runTest( - appDir: string, - recipeInstance: RecipeInstance, - test: TestDef, - envVars: Env, -): Promise { - const { recipe, label } = recipeInstance; - console.log(`Running test command for test application "${label}", test "${test.testName}"`); - - const env = { - ...process.env, - ...envVars, - }; - - const testResult = await spawnAsync(test.testCommand, { - cwd: appDir, - timeout: (recipe.testTimeoutSeconds ?? DEFAULT_TEST_TIMEOUT_SECONDS) * 1000, - env: { - ...env, - ...prefixObjectKeys(env, 'NEXT_PUBLIC_'), - ...prefixObjectKeys(env, 'REACT_APP_'), - }, - }); - - if (testResult.error) { - console.log(`Test failed for test application "${label}", test "${test.testName}"`); - - // Prepends some text to the output test command's output so we can distinguish it from logging in this script - console.log(testResult.stdout.replace(/^/gm, ' [TEST OUTPUT] ')); - console.log(testResult.stderr.replace(/^/gm, ' [TEST OUTPUT] ')); - - console.log('[TEST ERROR] ', testResult.error); - - return { - testName: test.testName, - result: testResult.error?.message.includes('ETDIMEDOUT') ? 'TIMEOUT' : 'FAIL', - }; - } - - return { - testName: test.testName, - result: 'PASS', - }; -} diff --git a/packages/e2e-tests/lib/types.ts b/packages/e2e-tests/lib/types.ts deleted file mode 100644 index f06094ce0426..000000000000 --- a/packages/e2e-tests/lib/types.ts +++ /dev/null @@ -1,50 +0,0 @@ -export type TestResult = { - testName: string; - result: 'PASS' | 'FAIL' | 'TIMEOUT'; -}; - -type DependencyOverrides = Record; - -export interface TestDef { - testName: string; - testCommand: string; - timeoutSeconds?: number; -} - -export interface RecipeInput { - testApplicationName: string; - buildCommand?: string; - buildAssertionCommand?: string; - buildTimeoutSeconds?: number; - testTimeoutSeconds?: number; - tests: TestDef[]; - versions?: { dependencyOverrides: DependencyOverrides }[]; - canaryVersions?: { dependencyOverrides: DependencyOverrides }[]; -} - -export interface Recipe { - path: string; - testApplicationName: string; - buildCommand?: string; - buildAssertionCommand?: string; - buildTimeoutSeconds?: number; - testTimeoutSeconds?: number; - tests: TestDef[]; - versions: { dependencyOverrides: DependencyOverrides }[]; -} - -export interface RecipeInstance { - label: string; - recipe: Recipe; - dependencyOverrides?: DependencyOverrides; - portModulo: number; - portGap: number; -} - -export interface RecipeTestResult extends RecipeInstance { - buildFailed: boolean; - testFailed: boolean; - tests: TestResult[]; -} - -export type Env = Record; diff --git a/packages/e2e-tests/lib/utils.ts b/packages/e2e-tests/lib/utils.ts deleted file mode 100644 index ef6d65dc84dc..000000000000 --- a/packages/e2e-tests/lib/utils.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* eslint-disable no-console */ -import * as childProcess from 'child_process'; - -// https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-error-message -export function printCIErrorMessage(message: string): void { - if (process.env.CI) { - console.log(`::error::${message}`); - } else { - console.log(message); - } -} - -interface SpawnAsync { - stdout: string; - stderr: string; - error?: Error; - status: number | null; -} - -interface SpawnOptions { - timeout?: number; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - env?: any; - cwd?: string; -} - -export function spawnAsync(cmd: string, options?: SpawnOptions, input?: string): Promise { - const timeoutMs = options?.timeout || 60_000 * 5; - - return new Promise(resolve => { - const cp = childProcess.spawn(cmd, { shell: true, ...options }); - - // Ensure we properly time out after max. 5 min per command - let timeout: ReturnType | undefined = setTimeout(() => { - console.log(`Command "${cmd}" timed out after 5 minutes.`); - cp.kill(); - end(null, `ETDIMEDOUT: Process timed out after ${timeoutMs} ms.`); - }, timeoutMs); - - const stderr: unknown[] = []; - const stdout: string[] = []; - let error: Error | undefined; - - function end(status: number | null, errorMessage?: string): void { - // This means we already ended - if (!timeout) { - return; - } - - if (!error && errorMessage) { - error = new Error(errorMessage); - } - - clearTimeout(timeout); - timeout = undefined; - resolve({ - stdout: stdout.join(''), - stderr: stderr.join(''), - error: error || (status !== 0 ? new Error(`Process exited with status ${status}`) : undefined), - status, - }); - } - - cp.stdout.on('data', data => { - stdout.push(data ? (data as object).toString() : ''); - }); - - cp.stderr.on('data', data => { - stderr.push(data ? (data as object).toString() : ''); - }); - - cp.on('error', e => { - error = e; - }); - - cp.on('close', status => { - end(status); - }); - - if (input) { - cp.stdin.write(input); - cp.stdin.end(); - } - }); -} - -export function prefixObjectKeys( - obj: Record, - prefix: string, -): Record { - return Object.keys(obj).reduce>((result, key) => { - result[prefix + key] = obj[key]; - return result; - }, {}); -} diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index cc5f6f0e5a1a..93471552edab 100644 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -18,13 +18,14 @@ "test:run": "ts-node run.ts", "test:validate-configuration": "ts-node validate-verdaccio-configuration.ts", "test:validate-test-app-setups": "ts-node validate-test-app-setups.ts", - "clean": "rimraf tmp test-applications/**/node_modules test-applications/**/dist" + "test:prepare": "ts-node prepare.ts", + "test:validate": "run-s test:validate-configuration test:validate-test-app-setups", + "clean": "rimraf tmp test-applications/**/{node_modules,dist,build,.next,.sveltekit,pnpm-lock.yaml}" }, "devDependencies": { "@types/glob": "8.0.0", "@types/node": "^14.6.4", "dotenv": "16.0.3", - "fs-extra": "11.1.0", "glob": "8.0.3", "ts-node": "10.9.1", "yaml": "2.2.2" diff --git a/packages/e2e-tests/prepare.ts b/packages/e2e-tests/prepare.ts new file mode 100644 index 000000000000..7fee7677bf94 --- /dev/null +++ b/packages/e2e-tests/prepare.ts @@ -0,0 +1,24 @@ +/* eslint-disable max-lines */ +/* eslint-disable no-console */ +import * as dotenv from 'dotenv'; + +import { validate } from './lib/validate'; +import { registrySetup } from './registrySetup'; + +async function run(): Promise { + // Load environment variables from .env file locally + dotenv.config(); + + if (!validate()) { + process.exit(1); + } + + try { + registrySetup(); + } catch (error) { + console.error(error); + process.exit(1); + } +} + +void run(); diff --git a/packages/e2e-tests/publish-packages.ts b/packages/e2e-tests/publish-packages.ts index fd03a777315a..4715b3258771 100644 --- a/packages/e2e-tests/publish-packages.ts +++ b/packages/e2e-tests/publish-packages.ts @@ -5,9 +5,6 @@ import * as path from 'path'; const repositoryRoot = path.resolve(__dirname, '../..'); -// Create tarballs -childProcess.execSync('yarn build:tarball', { encoding: 'utf8', cwd: repositoryRoot, stdio: 'inherit' }); - // Get absolute paths of all the packages we want to publish to the fake registry const packageTarballPaths = glob.sync('packages/*/sentry-*.tgz', { cwd: repositoryRoot, diff --git a/packages/e2e-tests/run.ts b/packages/e2e-tests/run.ts index 658b4ffaa97f..cdf400f52498 100644 --- a/packages/e2e-tests/run.ts +++ b/packages/e2e-tests/run.ts @@ -1,34 +1,52 @@ /* eslint-disable max-lines */ /* eslint-disable no-console */ +import { exec } from 'child_process'; import * as dotenv from 'dotenv'; -import * as glob from 'glob'; +import { sync as globSync } from 'glob'; +import { resolve } from 'path'; +import { promisify } from 'util'; -import { runAllTestApps } from './lib/runAllTestApps'; import { validate } from './lib/validate'; import { registrySetup } from './registrySetup'; +const asyncExec = promisify(exec); + async function run(): Promise { // Load environment variables from .env file locally dotenv.config(); + // Allow to run a single app only via `yarn test:run ` + const appName = process.argv[2]; + if (!validate()) { process.exit(1); } const envVarsToInject = { - REACT_APP_E2E_TEST_DSN: process.env.E2E_TEST_DSN, - REMIX_APP_E2E_TEST_DSN: process.env.E2E_TEST_DSN, NEXT_PUBLIC_E2E_TEST_DSN: process.env.E2E_TEST_DSN, PUBLIC_E2E_TEST_DSN: process.env.E2E_TEST_DSN, - BASE_PORT: '27496', // just some random port + REACT_APP_E2E_TEST_DSN: process.env.E2E_TEST_DSN, }; + const env = { ...process.env, ...envVarsToInject }; + try { registrySetup(); - const recipePaths = glob.sync(`${__dirname}/test-applications/*/test-recipe.json`, { absolute: true }); + const testAppPaths = appName ? [appName.trim()] : globSync('*', { cwd: `${__dirname}/test-applications/` }); + + console.log(`Runnings tests for: ${testAppPaths.join(', ')}`); + console.log(''); + + for (const testAppPath of testAppPaths) { + const cwd = resolve('test-applications', testAppPath); + + console.log(`Building ${testAppPath}...`); + await asyncExec('pnpm test:build', { env, cwd }); - await runAllTestApps(recipePaths, envVarsToInject); + console.log(`Testing ${testAppPath}...`); + await asyncExec('pnpm test:assert', { env, cwd }); + } } catch (error) { console.error(error); process.exit(1); diff --git a/packages/e2e-tests/test-applications/create-next-app/package.json b/packages/e2e-tests/test-applications/create-next-app/package.json index 2935a28b9dd3..b4f971150d8f 100644 --- a/packages/e2e-tests/test-applications/create-next-app/package.json +++ b/packages/e2e-tests/test-applications/create-next-app/package.json @@ -5,7 +5,9 @@ "scripts": { "build": "next build", "test:prod": "TEST_ENV=prod playwright test", - "test:dev": "TEST_ENV=dev playwright test" + "test:dev": "TEST_ENV=dev playwright test", + "test:build": "pnpm install && npx playwright install && pnpm build", + "test:assert": "pnpm test:prod && pnpm test:dev" }, "dependencies": { "@next/font": "13.0.7", diff --git a/packages/e2e-tests/test-applications/create-next-app/playwright.config.ts b/packages/e2e-tests/test-applications/create-next-app/playwright.config.ts index 22dddd16d7ba..dafe32a31126 100644 --- a/packages/e2e-tests/test-applications/create-next-app/playwright.config.ts +++ b/packages/e2e-tests/test-applications/create-next-app/playwright.config.ts @@ -7,7 +7,7 @@ if (!testEnv) { throw new Error('No test env defined'); } -const port = Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO); +const port = 3030; /** * See https://playwright.dev/docs/test-configuration. diff --git a/packages/e2e-tests/test-applications/create-next-app/sentry.client.config.ts b/packages/e2e-tests/test-applications/create-next-app/sentry.client.config.ts index 48ca1cd7306b..760843e01739 100644 --- a/packages/e2e-tests/test-applications/create-next-app/sentry.client.config.ts +++ b/packages/e2e-tests/test-applications/create-next-app/sentry.client.config.ts @@ -9,6 +9,7 @@ Sentry.init({ dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, // Adjust this value in production, or use tracesSampler for greater control tracesSampleRate: 1.0, + // ... // Note: if you want to override the automatic release value, do not set a // `release` value here - use the environment variable `SENTRY_RELEASE`, so diff --git a/packages/e2e-tests/test-applications/create-next-app/test-recipe.json b/packages/e2e-tests/test-applications/create-next-app/test-recipe.json deleted file mode 100644 index 621c79df3cc7..000000000000 --- a/packages/e2e-tests/test-applications/create-next-app/test-recipe.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "../../test-recipe-schema.json", - "testApplicationName": "create-next-app", - "buildCommand": "pnpm install && npx playwright install && pnpm build", - "tests": [ - { - "testName": "Playwright tests - Prod Mode", - "testCommand": "pnpm test:prod" - }, - { - "testName": "Playwright tests - Dev Mode", - "testCommand": "pnpm test:dev" - } - ] -} diff --git a/packages/e2e-tests/test-applications/create-next-app/tsconfig.json b/packages/e2e-tests/test-applications/create-next-app/tsconfig.json index 7686eb53f6ac..3ff0501fdb85 100644 --- a/packages/e2e-tests/test-applications/create-next-app/tsconfig.json +++ b/packages/e2e-tests/test-applications/create-next-app/tsconfig.json @@ -12,7 +12,8 @@ "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve" + "jsx": "preserve", + "incremental": true }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next.config.js"], "exclude": ["node_modules"] diff --git a/packages/e2e-tests/test-applications/create-react-app/package.json b/packages/e2e-tests/test-applications/create-react-app/package.json index 2ba893bc70ed..8584121f92dc 100644 --- a/packages/e2e-tests/test-applications/create-react-app/package.json +++ b/packages/e2e-tests/test-applications/create-react-app/package.json @@ -22,7 +22,11 @@ "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "test:build": "pnpm install && pnpm build", + "test:build-ts3.8": "pnpm install && pnpm add typescript@3.8 && pnpm build", + "test:build-canary": "pnpm install && pnpm add react@canary react-dom@canary && pnpm build", + "test:assert": "pnpm -v" }, "eslintConfig": { "extends": [ diff --git a/packages/e2e-tests/test-applications/create-react-app/test-recipe.json b/packages/e2e-tests/test-applications/create-react-app/test-recipe.json deleted file mode 100644 index f0f5e19553cc..000000000000 --- a/packages/e2e-tests/test-applications/create-react-app/test-recipe.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "../../test-recipe-schema.json", - "testApplicationName": "create-react-app", - "buildCommand": "pnpm install && pnpm build", - "tests": [], - "versions": [ - { - "dependencyOverrides": { - "typescript": "3.8.3" - } - } - ], - "canaryVersions": [ - { - "dependencyOverrides": { - "react": "canary", - "react-dom": "canary" - } - } - ] -} diff --git a/packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx b/packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx index edb18224d6c6..a4d1618fd976 100644 --- a/packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx +++ b/packages/e2e-tests/test-applications/create-remix-app/app/entry.client.tsx @@ -10,7 +10,7 @@ import { hydrateRoot } from 'react-dom/client'; import * as Sentry from '@sentry/remix'; Sentry.init({ - dsn: process.env.REMIX_APP_E2E_TEST_DSN, + dsn: process.env.E2E_TEST_DSN, integrations: [ new Sentry.BrowserTracing({ routingInstrumentation: Sentry.remixRouterInstrumentation(useEffect, useLocation, useMatches), diff --git a/packages/e2e-tests/test-applications/create-remix-app/app/entry.server.tsx b/packages/e2e-tests/test-applications/create-remix-app/app/entry.server.tsx index c9975e940e21..62dd7ea43df2 100644 --- a/packages/e2e-tests/test-applications/create-remix-app/app/entry.server.tsx +++ b/packages/e2e-tests/test-applications/create-remix-app/app/entry.server.tsx @@ -16,7 +16,7 @@ import * as Sentry from '@sentry/remix'; const ABORT_DELAY = 5_000; Sentry.init({ - dsn: process.env.REMIX_APP_E2E_TEST_DSN, + dsn: process.env.E2E_TEST_DSN, // Performance Monitoring tracesSampleRate: 1.0, // Capture 100% of the transactions, reduce in production! }); diff --git a/packages/e2e-tests/test-applications/create-remix-app/package.json b/packages/e2e-tests/test-applications/create-remix-app/package.json index ce7b8cd8456c..a2b64bbe0ffc 100644 --- a/packages/e2e-tests/test-applications/create-remix-app/package.json +++ b/packages/e2e-tests/test-applications/create-remix-app/package.json @@ -5,7 +5,9 @@ "build": "remix build", "dev": "remix dev", "start": "remix-serve build", - "typecheck": "tsc" + "typecheck": "tsc", + "test:build": "pnpm install && npx playwright install && pnpm build", + "test:assert": "pnpm playwright test" }, "dependencies": { "@sentry/remix": "latest || *", @@ -18,6 +20,7 @@ "react-dom": "^18.2.0" }, "devDependencies": { + "@playwright/test": "^1.36.2", "@remix-run/dev": "^1.16.1", "@remix-run/eslint-config": "^1.16.1", "@types/react": "^18.0.35", diff --git a/packages/e2e-tests/test-applications/create-remix-app/playwright.config.ts b/packages/e2e-tests/test-applications/create-remix-app/playwright.config.ts new file mode 100644 index 000000000000..b35659cecdb5 --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/playwright.config.ts @@ -0,0 +1,69 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; + +const port = 3030; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: './tests', + /* Maximum time one test can run for. */ + timeout: 60 * 1000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 5000, + }, + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: 0, + /* Opt out of parallel tests on CI. */ + workers: 1, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'list', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + // For now we only test Chrome! + // { + // name: 'firefox', + // use: { + // ...devices['Desktop Firefox'], + // }, + // }, + // { + // name: 'webkit', + // use: { + // ...devices['Desktop Safari'], + // }, + // }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: `PORT=${port} pnpm start`, + port, + }, +}; + +export default config; diff --git a/packages/e2e-tests/test-applications/create-remix-app/test-recipe.json b/packages/e2e-tests/test-applications/create-remix-app/test-recipe.json deleted file mode 100644 index dc16b21af54c..000000000000 --- a/packages/e2e-tests/test-applications/create-remix-app/test-recipe.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "../../test-recipe-schema.json", - "testApplicationName": "create-remix-app", - "buildCommand": "pnpm install && pnpm build", - "tests": [] -} diff --git a/packages/e2e-tests/test-applications/create-remix-app/tests/behaviour-client.test.ts b/packages/e2e-tests/test-applications/create-remix-app/tests/behaviour-client.test.ts new file mode 100644 index 000000000000..9b5861619bec --- /dev/null +++ b/packages/e2e-tests/test-applications/create-remix-app/tests/behaviour-client.test.ts @@ -0,0 +1,7 @@ +import { test, expect } from '@playwright/test'; + +test('Boots up correctly', async ({ page }) => { + await page.goto('/'); + + await expect(page.getByRole('heading')).toHaveText('Welcome to Remix'); +}); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/assert-build.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/assert-build.ts index cb7adf38cde8..7476b17c91da 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/assert-build.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/assert-build.ts @@ -1,16 +1,16 @@ import * as fs from 'fs'; import * as assert from 'assert/strict'; -const stdin = fs.readFileSync(0).toString(); +const buildOutput = fs.readFileSync('.tmp_build_output', 'utf-8'); -// Assert that all static components stay static and ally dynamic components stay dynamic +// Assert that all static components stay static and all dynamic components stay dynamic -assert.match(stdin, /○ \/client-component/); -assert.match(stdin, /● \/client-component\/parameter\/\[\.\.\.parameters\]/); -assert.match(stdin, /● \/client-component\/parameter\/\[parameter\]/); +assert.match(buildOutput, /○ \/client-component/); +assert.match(buildOutput, /● \/client-component\/parameter\/\[\.\.\.parameters\]/); +assert.match(buildOutput, /● \/client-component\/parameter\/\[parameter\]/); -assert.match(stdin, /λ \/server-component/); -assert.match(stdin, /λ \/server-component\/parameter\/\[\.\.\.parameters\]/); -assert.match(stdin, /λ \/server-component\/parameter\/\[parameter\]/); +assert.match(buildOutput, /λ \/server-component/); +assert.match(buildOutput, /λ \/server-component\/parameter\/\[\.\.\.parameters\]/); +assert.match(buildOutput, /λ \/server-component\/parameter\/\[parameter\]/); export {}; diff --git a/packages/e2e-tests/test-utils/event-proxy-server.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/event-proxy-server.ts similarity index 100% rename from packages/e2e-tests/test-utils/event-proxy-server.ts rename to packages/e2e-tests/test-applications/nextjs-app-dir/event-proxy-server.ts diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/package.json b/packages/e2e-tests/test-applications/nextjs-app-dir/package.json index 58e4ac82b793..a5ce5514e3ca 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/package.json +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/package.json @@ -3,9 +3,14 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "next build", + "build": "next build > .tmp_build_output", "test:prod": "TEST_ENV=production playwright test", - "test:dev": "TEST_ENV=development playwright test" + "test:dev": "TEST_ENV=development playwright test", + "test:build": "pnpm install && npx playwright install && pnpm build", + "test:test-build": "pnpm ts-node --script-mode assert-build.ts", + "test:build-canary": "pnpm install && pnpm add next@canary && npx playwright install && pnpm build", + "test:build-latest": "pnpm install && pnpm add next@latest && npx playwright install && pnpm build", + "test:assert": "pnpm test:test-build && pnpm test:prod && pnpm test:dev" }, "dependencies": { "@next/font": "13.0.7", @@ -21,6 +26,10 @@ "ts-node": "10.9.1", "@playwright/test": "^1.27.1" }, + "devDependencies": { + "@sentry/types": "latest || *", + "@sentry/utils": "latest || *" + }, "volta": { "node": "16.19.0", "yarn": "1.22.19" diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/playwright.config.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/playwright.config.ts index d151f812ee38..c0c413c0627b 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/playwright.config.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/playwright.config.ts @@ -7,8 +7,8 @@ if (!testEnv) { throw new Error('No test env defined'); } -const nextPort = Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO); -const eventProxyPort = Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO) + Number(process.env.PORT_GAP); +const nextPort = 3030; +const eventProxyPort = 3031; /** * See https://playwright.dev/docs/test-configuration. diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.client.config.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.client.config.ts index 11c76e31b460..ad780407a5b7 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.client.config.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.client.config.ts @@ -3,10 +3,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, - tunnel: `http://localhost:${ - Number(process.env.NEXT_PUBLIC_BASE_PORT) + - Number(process.env.NEXT_PUBLIC_PORT_MODULO) + - Number(process.env.NEXT_PUBLIC_PORT_GAP) - }/`, // proxy server + tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, }); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.edge.config.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.edge.config.ts index 11c76e31b460..ad780407a5b7 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.edge.config.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.edge.config.ts @@ -3,10 +3,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, - tunnel: `http://localhost:${ - Number(process.env.NEXT_PUBLIC_BASE_PORT) + - Number(process.env.NEXT_PUBLIC_PORT_MODULO) + - Number(process.env.NEXT_PUBLIC_PORT_GAP) - }/`, // proxy server + tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, }); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.server.config.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.server.config.ts index 11c76e31b460..ad780407a5b7 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.server.config.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/sentry.server.config.ts @@ -3,10 +3,6 @@ import * as Sentry from '@sentry/nextjs'; Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, - tunnel: `http://localhost:${ - Number(process.env.NEXT_PUBLIC_BASE_PORT) + - Number(process.env.NEXT_PUBLIC_PORT_MODULO) + - Number(process.env.NEXT_PUBLIC_PORT_GAP) - }/`, // proxy server + tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, }); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/start-event-proxy.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/start-event-proxy.ts index 705af6e98a7a..ef8d204c5224 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/start-event-proxy.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/start-event-proxy.ts @@ -1,6 +1,6 @@ -import { startEventProxyServer } from '../../test-utils/event-proxy-server'; +import { startEventProxyServer } from './event-proxy-server'; startEventProxyServer({ - port: Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO) + Number(process.env.PORT_GAP), + port: 3031, proxyServerName: 'nextjs-13-app-dir', }); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/test-recipe.json b/packages/e2e-tests/test-applications/nextjs-app-dir/test-recipe.json deleted file mode 100644 index 134eecd517e1..000000000000 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/test-recipe.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "$schema": "../../test-recipe-schema.json", - "testApplicationName": "nextjs-13-app-dir", - "buildCommand": "pnpm install && npx playwright install && pnpm build", - "buildAssertionCommand": "pnpm ts-node --script-mode assert-build.ts", - "tests": [ - { - "testName": "Prod Mode", - "testCommand": "pnpm test:prod" - }, - { - "testName": "Dev Mode", - "testCommand": "pnpm test:dev" - } - ], - "canaryVersions": [ - { - "dependencyOverrides": { - "next": "latest || *" - } - }, - { - "dependencyOverrides": { - "next": "canary" - } - } - ] -} diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts index f4841c2a6b16..03dd1b978714 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/devErrorSymbolification.test.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { waitForError } from '../../../test-utils/event-proxy-server'; +import { waitForError } from '../event-proxy-server'; test.describe('dev mode error symbolification', () => { if (process.env.TEST_ENV !== 'development') { diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts index 8f73764a919f..7d8d2a2fe97a 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge-route.test.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { waitForTransaction, waitForError } from '../../../test-utils/event-proxy-server'; +import { waitForTransaction, waitForError } from '../event-proxy-server'; test('Should create a transaction for edge routes', async ({ request }) => { test.skip(process.env.TEST_ENV === 'development', "Doesn't work in dev mode."); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/exceptions.test.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/exceptions.test.ts index ea96490b79ce..d31e869fd8b3 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/exceptions.test.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/exceptions.test.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { waitForError } from '../../../test-utils/event-proxy-server'; +import { waitForError } from '../event-proxy-server'; import axios, { AxiosError } from 'axios'; const authToken = process.env.E2E_TEST_AUTH_TOKEN; diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts index 268a55f1f481..95af8a7a8f29 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/middleware.test.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { waitForTransaction, waitForError } from '../../../test-utils/event-proxy-server'; +import { waitForTransaction, waitForError } from '../event-proxy-server'; test('Should create a transaction for middleware', async ({ request }) => { test.skip(process.env.TEST_ENV === 'development', "Doesn't work in dev mode."); diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/trace.test.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/trace.test.ts index 99d03266d01f..ed9a68513a19 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/trace.test.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/trace.test.ts @@ -1,5 +1,5 @@ import { test } from '@playwright/test'; -import { waitForTransaction } from '../../../test-utils/event-proxy-server'; +import { waitForTransaction } from '../event-proxy-server'; if (process.env.TEST_ENV === 'production') { // TODO: Fix that this is flakey on dev server - might be an SDK bug diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts index 8dac4e58e5ba..8413d623eb5c 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tests/transactions.test.ts @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { waitForTransaction } from '../../../test-utils/event-proxy-server'; +import { waitForTransaction } from '../event-proxy-server'; import axios, { AxiosError } from 'axios'; const authToken = process.env.E2E_TEST_AUTH_TOKEN; diff --git a/packages/e2e-tests/test-applications/nextjs-app-dir/tsconfig.json b/packages/e2e-tests/test-applications/nextjs-app-dir/tsconfig.json index e78ff0d9c0e7..60825545944d 100644 --- a/packages/e2e-tests/test-applications/nextjs-app-dir/tsconfig.json +++ b/packages/e2e-tests/test-applications/nextjs-app-dir/tsconfig.json @@ -17,7 +17,8 @@ { "name": "next" } - ] + ], + "incremental": true }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "next.config.js", ".next/types/**/*.ts"], "exclude": ["node_modules"], diff --git a/packages/e2e-tests/test-applications/node-express-app/package.json b/packages/e2e-tests/test-applications/node-express-app/package.json index ffd74762f286..45efd817e901 100644 --- a/packages/e2e-tests/test-applications/node-express-app/package.json +++ b/packages/e2e-tests/test-applications/node-express-app/package.json @@ -5,7 +5,9 @@ "scripts": { "build": "tsc", "start": "node dist/app.js", - "test": "playwright test" + "test": "playwright test", + "test:build": "pnpm install && pnpm build", + "test:assert": "pnpm test" }, "dependencies": { "@sentry/integrations": "latest || *", diff --git a/packages/e2e-tests/test-applications/node-express-app/playwright.config.ts b/packages/e2e-tests/test-applications/node-express-app/playwright.config.ts index d0544001644b..879d7b3d2093 100644 --- a/packages/e2e-tests/test-applications/node-express-app/playwright.config.ts +++ b/packages/e2e-tests/test-applications/node-express-app/playwright.config.ts @@ -58,7 +58,7 @@ const config: PlaywrightTestConfig = { /* Run your local dev server before starting the tests */ webServer: { command: 'pnpm start', - port: Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO), + port: 3030, }, }; diff --git a/packages/e2e-tests/test-applications/node-express-app/src/app.ts b/packages/e2e-tests/test-applications/node-express-app/src/app.ts index 1035a4958762..16aa82545b85 100644 --- a/packages/e2e-tests/test-applications/node-express-app/src/app.ts +++ b/packages/e2e-tests/test-applications/node-express-app/src/app.ts @@ -11,14 +11,14 @@ declare global { Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions - dsn: process.env.NEXT_PUBLIC_E2E_TEST_DSN, + dsn: process.env.E2E_TEST_DSN, integrations: [new Integrations.HttpClient()], debug: true, tracesSampleRate: 1, }); const app = express(); -const port = Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO); +const port = 3030; app.use(Sentry.Handlers.requestHandler()); app.use(Sentry.Handlers.tracingHandler()); diff --git a/packages/e2e-tests/test-applications/node-express-app/test-recipe.json b/packages/e2e-tests/test-applications/node-express-app/test-recipe.json deleted file mode 100644 index 08d5993e1c32..000000000000 --- a/packages/e2e-tests/test-applications/node-express-app/test-recipe.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "../../test-recipe-schema.json", - "testApplicationName": "node-express-app", - "buildCommand": "pnpm install && pnpm build", - "tests": [ - { - "testName": "Test express server", - "testCommand": "pnpm test", - "timeoutSeconds": 60 - } - ] -} diff --git a/packages/e2e-tests/test-applications/react-create-hash-router/package.json b/packages/e2e-tests/test-applications/react-create-hash-router/package.json index fbd4f9017c50..c3bfaf4c9e0c 100644 --- a/packages/e2e-tests/test-applications/react-create-hash-router/package.json +++ b/packages/e2e-tests/test-applications/react-create-hash-router/package.json @@ -21,7 +21,10 @@ "scripts": { "build": "react-scripts build", "start": "serve -s build", - "test": "playwright test" + "test": "playwright test", + "test:build": "pnpm install && 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": [ diff --git a/packages/e2e-tests/test-applications/react-create-hash-router/playwright.config.ts b/packages/e2e-tests/test-applications/react-create-hash-router/playwright.config.ts index a24d7bc1c742..c68482378d7a 100644 --- a/packages/e2e-tests/test-applications/react-create-hash-router/playwright.config.ts +++ b/packages/e2e-tests/test-applications/react-create-hash-router/playwright.config.ts @@ -60,9 +60,9 @@ const config: PlaywrightTestConfig = { /* Run your local dev server before starting the tests */ webServer: { command: 'pnpm start', - port: Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO), + port: 3030, env: { - PORT: String(Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO)), + PORT: '3030', }, }, }; diff --git a/packages/e2e-tests/test-applications/react-create-hash-router/src/index.tsx b/packages/e2e-tests/test-applications/react-create-hash-router/src/index.tsx index aef574bce3c4..643541357342 100644 --- a/packages/e2e-tests/test-applications/react-create-hash-router/src/index.tsx +++ b/packages/e2e-tests/test-applications/react-create-hash-router/src/index.tsx @@ -37,6 +37,8 @@ Sentry.init({ // Always capture replays, so we can test this properly replaysSessionSampleRate: 1.0, replaysOnErrorSampleRate: 0.0, + + debug: true, }); Object.defineProperty(window, 'sentryReplayId', { diff --git a/packages/e2e-tests/test-applications/react-create-hash-router/test-recipe.json b/packages/e2e-tests/test-applications/react-create-hash-router/test-recipe.json deleted file mode 100644 index 2fa5a9b823aa..000000000000 --- a/packages/e2e-tests/test-applications/react-create-hash-router/test-recipe.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "../../test-recipe-schema.json", - "testApplicationName": "react-create-hash-router", - "buildCommand": "pnpm install && npx playwright install && pnpm build", - "tests": [ - { - "testName": "Playwright tests", - "testCommand": "pnpm test" - } - ], - "canaryVersions": [ - { - "dependencyOverrides": { - "react": "latest || *", - "react-dom": "latest || *" - } - } - ] -} diff --git a/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/package.json b/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/package.json index f024d9426fb3..8ad3f32e13e9 100644 --- a/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/package.json +++ b/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/package.json @@ -22,7 +22,9 @@ "scripts": { "build": "react-scripts build", "start": "serve -s build", - "test": "playwright test" + "test": "playwright test", + "test:build": "pnpm install && npx playwright install && pnpm build", + "test:assert": "pnpm test" }, "eslintConfig": { "extends": [ diff --git a/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/playwright.config.ts b/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/playwright.config.ts index a24d7bc1c742..c68482378d7a 100644 --- a/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/playwright.config.ts +++ b/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/playwright.config.ts @@ -60,9 +60,9 @@ const config: PlaywrightTestConfig = { /* Run your local dev server before starting the tests */ webServer: { command: 'pnpm start', - port: Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO), + port: 3030, env: { - PORT: String(Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO)), + PORT: '3030', }, }, }; diff --git a/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/test-recipe.json b/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/test-recipe.json deleted file mode 100644 index 3f38669e0587..000000000000 --- a/packages/e2e-tests/test-applications/standard-frontend-react-tracing-import/test-recipe.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "$schema": "../../test-recipe-schema.json", - "testApplicationName": "standard-frontend-react-tracing-import", - "buildCommand": "pnpm install && npx playwright install && pnpm build", - "tests": [ - { - "testName": "Playwright tests", - "testCommand": "pnpm test" - } - ], - "canaryVersions": [] -} diff --git a/packages/e2e-tests/test-applications/standard-frontend-react/package.json b/packages/e2e-tests/test-applications/standard-frontend-react/package.json index 875fa3157b5a..554821b1366c 100644 --- a/packages/e2e-tests/test-applications/standard-frontend-react/package.json +++ b/packages/e2e-tests/test-applications/standard-frontend-react/package.json @@ -21,7 +21,11 @@ "scripts": { "build": "react-scripts build", "start": "serve -s build", - "test": "playwright test" + "test": "playwright test", + "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": [ diff --git a/packages/e2e-tests/test-applications/standard-frontend-react/playwright.config.ts b/packages/e2e-tests/test-applications/standard-frontend-react/playwright.config.ts index a24d7bc1c742..c68482378d7a 100644 --- a/packages/e2e-tests/test-applications/standard-frontend-react/playwright.config.ts +++ b/packages/e2e-tests/test-applications/standard-frontend-react/playwright.config.ts @@ -60,9 +60,9 @@ const config: PlaywrightTestConfig = { /* Run your local dev server before starting the tests */ webServer: { command: 'pnpm start', - port: Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO), + port: 3030, env: { - PORT: String(Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO)), + PORT: '3030', }, }, }; diff --git a/packages/e2e-tests/test-applications/standard-frontend-react/test-recipe.json b/packages/e2e-tests/test-applications/standard-frontend-react/test-recipe.json deleted file mode 100644 index c0ac9c697a1d..000000000000 --- a/packages/e2e-tests/test-applications/standard-frontend-react/test-recipe.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "../../test-recipe-schema.json", - "testApplicationName": "standard-frontend-react", - "buildCommand": "pnpm install && npx playwright install && pnpm build", - "tests": [ - { - "testName": "Playwright tests", - "testCommand": "pnpm test" - } - ], - "versions": [ - { - "dependencyOverrides": { - "typescript": "3.8.3" - } - } - ], - "canaryVersions": [ - { - "dependencyOverrides": { - "react": "latest || *", - "react-dom": "latest || *" - } - } - ] -} diff --git a/packages/e2e-tests/test-applications/sveltekit/event-proxy-server.ts b/packages/e2e-tests/test-applications/sveltekit/event-proxy-server.ts index 34d77e629e1e..c61e20d4081d 100644 --- a/packages/e2e-tests/test-applications/sveltekit/event-proxy-server.ts +++ b/packages/e2e-tests/test-applications/sveltekit/event-proxy-server.ts @@ -1,18 +1,3 @@ -// This is vendored from packages/e2e-tests/test-utils/event-proxy-server.ts -// This is done because otherwise we were getting `ERR_MODULE_NOT_FOUND`: -// -// CustomError: Cannot find module '/sentry-javascript/packages/e2e-tests/test-utils/event-proxy-server' imported from /sentry-javascript/packages/e2e-tests/test-applications/sveltekit/start-event-proxy.ts -// at finalizeResolution (/sentry-javascript/packages/e2e-tests/test-applications/sveltekit/node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:366:11) -// at moduleResolve (/sentry-javascript/packages/e2e-tests/test-applications/sveltekit/node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:801:10) -// at Object.defaultResolve (/sentry-javascript/packages/e2e-tests/test-applications/sveltekit/node_modules/ts-node/dist-raw/node-internal-modules-esm-resolve.js:912:11) -// at /sentry-javascript/packages/e2e-tests/test-applications/sveltekit/node_modules/ts-node/src/esm.ts:218:35 -// at entrypointFallback (/sentry-javascript/packages/e2e-tests/test-applications/sveltekit/node_modules/ts-node/src/esm.ts:168:34) -// at /sentry-javascript/packages/e2e-tests/test-applications/sveltekit/node_modules/ts-node/src/esm.ts:217:14 -// at addShortCircuitFlag (/sentry-javascript/packages/e2e-tests/test-applications/sveltekit/node_modules/ts-node/src/esm.ts:409:21) -// at resolve (/sentry-javascript/packages/e2e-tests/test-applications/sveltekit/node_modules/ts-node/src/esm.ts:197:12) -// at resolve (/sentry-javascript/packages/e2e-tests/test-applications/sveltekit/node_modules/ts-node/src/child/child-loader.ts:15:39) -// at nextResolve (node:internal/modules/esm/loader:163:28) - import type { Envelope, EnvelopeItem, Event } from '@sentry/types'; import { parseEnvelope } from '@sentry/utils'; import * as fs from 'fs'; diff --git a/packages/e2e-tests/test-applications/sveltekit/package.json b/packages/e2e-tests/test-applications/sveltekit/package.json index 37fa0663edb7..4c644d83127a 100644 --- a/packages/e2e-tests/test-applications/sveltekit/package.json +++ b/packages/e2e-tests/test-applications/sveltekit/package.json @@ -9,13 +9,17 @@ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", "test:prod": "TEST_ENV=production playwright test", - "test:dev": "TEST_ENV=development playwright test" + "test:dev": "TEST_ENV=development playwright test", + "test:build": "pnpm install && pnpm build", + "test:assert": "pnpm -v" }, "dependencies": { "@sentry/sveltekit": "latest || *" }, "devDependencies": { "@playwright/test": "^1.27.1", + "@sentry/types": "latest || *", + "@sentry/utils": "latest || *", "@sveltejs/adapter-auto": "^2.0.0", "@sveltejs/adapter-node": "^1.2.4", "@sveltejs/kit": "^1.5.0", diff --git a/packages/e2e-tests/test-applications/sveltekit/playwright.config.ts b/packages/e2e-tests/test-applications/sveltekit/playwright.config.ts index edeec487f24c..e38177e51a90 100644 --- a/packages/e2e-tests/test-applications/sveltekit/playwright.config.ts +++ b/packages/e2e-tests/test-applications/sveltekit/playwright.config.ts @@ -7,7 +7,7 @@ if (!testEnv) { throw new Error('No test env defined'); } -const port = Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO); +const port = 3030; /** * See https://playwright.dev/docs/test-configuration. @@ -56,7 +56,7 @@ const config: PlaywrightTestConfig = { webServer: [ { command: 'pnpm ts-node --esm start-event-proxy.ts', - port: Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO) + Number(process.env.PORT_GAP), + port: 3031, }, { command: diff --git a/packages/e2e-tests/test-applications/sveltekit/src/hooks.client.ts b/packages/e2e-tests/test-applications/sveltekit/src/hooks.client.ts index 243813eda7b8..bfe90b150886 100644 --- a/packages/e2e-tests/test-applications/sveltekit/src/hooks.client.ts +++ b/packages/e2e-tests/test-applications/sveltekit/src/hooks.client.ts @@ -5,9 +5,7 @@ Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: env.PUBLIC_E2E_TEST_DSN, debug: true, - tunnel: `http://localhost:${ - Number(env.PUBLIC_BASE_PORT) + Number(env.PUBLIC_PORT_MODULO) + Number(env.PUBLIC_PORT_GAP) - }/`, // proxy server + tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, }); diff --git a/packages/e2e-tests/test-applications/sveltekit/src/hooks.server.ts b/packages/e2e-tests/test-applications/sveltekit/src/hooks.server.ts index f049ff9b48f0..2d9cb9b02328 100644 --- a/packages/e2e-tests/test-applications/sveltekit/src/hooks.server.ts +++ b/packages/e2e-tests/test-applications/sveltekit/src/hooks.server.ts @@ -5,7 +5,7 @@ Sentry.init({ environment: 'qa', // dynamic sampling bias to keep transactions dsn: env.E2E_TEST_DSN, debug: true, - tunnel: `http://localhost:${Number(env.BASE_PORT) + Number(env.PORT_MODULO) + Number(env.PORT_GAP)}/`, // proxy server + tunnel: `http://localhost:3031/`, // proxy server tracesSampleRate: 1.0, }); diff --git a/packages/e2e-tests/test-applications/sveltekit/start-event-proxy.ts b/packages/e2e-tests/test-applications/sveltekit/start-event-proxy.ts index cac3cea3bacd..db7640e0d825 100644 --- a/packages/e2e-tests/test-applications/sveltekit/start-event-proxy.ts +++ b/packages/e2e-tests/test-applications/sveltekit/start-event-proxy.ts @@ -1,6 +1,6 @@ import { startEventProxyServer } from './event-proxy-server'; startEventProxyServer({ - port: Number(process.env.BASE_PORT) + Number(process.env.PORT_MODULO) + Number(process.env.PORT_GAP), + port: 3031, proxyServerName: 'sveltekit', }); diff --git a/packages/e2e-tests/test-applications/sveltekit/test-recipe.json b/packages/e2e-tests/test-applications/sveltekit/test-recipe.json deleted file mode 100644 index 4060ceedd815..000000000000 --- a/packages/e2e-tests/test-applications/sveltekit/test-recipe.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$schema": "../../test-recipe-schema.json", - "testApplicationName": "Sveltekit", - "buildCommand": "pnpm install && pnpm build", - "tests": [] -} diff --git a/packages/e2e-tests/test-recipe-schema.json b/packages/e2e-tests/test-recipe-schema.json deleted file mode 100644 index ee14c2eb4f82..000000000000 --- a/packages/e2e-tests/test-recipe-schema.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "title": "Sentry JavaScript E2E Test Recipe", - "type": "object", - "properties": { - "testApplicationName": { - "type": "string", - "description": "Name displayed in test output" - }, - "buildCommand": { - "type": "string", - "description": "Command that is run to install dependencies and build the test application. This command is only run once before all tests. Working directory of the command is the root of the test application." - }, - "buildAssertionCommand": { - "type": "string", - "description": "Command to verify build output. This command will be run after the build is complete. The command will receive the STDOUT of the `buildCommand` as STDIN." - }, - "buildTimeoutSeconds": { - "type": "number", - "description": "Timeout for the build command in seconds. Default: 5 minutes" - }, - "testTimeoutSeconds": { - "type": "number", - "description": "Timeout for the test command in seconds. Default: 2 minutes" - }, - "tests": { - "type": "array", - "description": "Tests to run in this test application", - "items": { - "type": "object", - "properties": { - "testName": { - "type": "string", - "description": "Name displayed in test output" - }, - "testCommand": { - "type": "string", - "description": "Command that is run to start the test. Working directory of the command is the root of the test application. If this command returns a non-zero exit code the test counts as failed." - }, - "timeoutSeconds": { - "type": "number", - "description": "Test timeout in seconds. Default: 60" - } - }, - "required": ["testName", "testCommand"] - } - }, - "versions": { - "type": "array", - "description": "List of different dependency versions to test. If this property is provided, the tests will run for each item, if it is omitted, the tests will only run once with the dependencies provided in package.json.", - "items": { - "type": "object", - "properties": { - "dependencyOverrides": { - "type": "object", - "description": "Object to merge with the \"dependencies\" object in package.json.", - "additionalProperties": { "type": "string" } - } - } - } - }, - "canaryVersions": { - "type": "array", - "description": "List of different dependency versions to test as part of the canary test. Canary tests are only run if the CANARY_E2E_TEST environment variable is set. If If CANARY_E2E_TEST is set and this property is provided, the tests will run for each item, if it is omitted, no tests will be executed.", - "items": { - "type": "object", - "properties": { - "dependencyOverrides": { - "type": "object", - "description": "Object to merge with the \"dependencies\" object in package.json.", - "additionalProperties": { "type": "string" } - } - } - } - } - }, - "required": ["testApplicationName", "tests"] -} diff --git a/packages/nextjs/src/config/webpack.ts b/packages/nextjs/src/config/webpack.ts index 786ce0dfaaed..49f450bb27a4 100644 --- a/packages/nextjs/src/config/webpack.ts +++ b/packages/nextjs/src/config/webpack.ts @@ -110,7 +110,7 @@ export function constructWebpackConfigFunction( appDirPath = path.join(projectDir, 'src', 'app'); } - const apiRoutesPath = path.join(pagesDirPath, 'api', '/'); + const apiRoutesPath = path.join(pagesDirPath, 'api'); const middlewareJsPath = path.join(pagesDirPath, '..', 'middleware.js'); const middlewareTsPath = path.join(pagesDirPath, '..', 'middleware.ts'); @@ -148,8 +148,8 @@ export function constructWebpackConfigFunction( test: resourcePath => { const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath); return ( - normalizedAbsoluteResourcePath.startsWith(pagesDirPath) && - !normalizedAbsoluteResourcePath.startsWith(apiRoutesPath) && + normalizedAbsoluteResourcePath.startsWith(pagesDirPath + path.sep) && + !normalizedAbsoluteResourcePath.startsWith(apiRoutesPath + path.sep) && dotPrefixedPageExtensions.some(ext => normalizedAbsoluteResourcePath.endsWith(ext)) ); }, @@ -193,7 +193,7 @@ export function constructWebpackConfigFunction( test: resourcePath => { const normalizedAbsoluteResourcePath = normalizeLoaderResourcePath(resourcePath); return ( - normalizedAbsoluteResourcePath.startsWith(apiRoutesPath) && + normalizedAbsoluteResourcePath.startsWith(apiRoutesPath + path.sep) && dotPrefixedPageExtensions.some(ext => normalizedAbsoluteResourcePath.endsWith(ext)) ); }, @@ -238,7 +238,7 @@ export function constructWebpackConfigFunction( // ".js, .jsx, or .tsx file extensions can be used for Pages" // https://beta.nextjs.org/docs/routing/pages-and-layouts#pages:~:text=.js%2C%20.jsx%2C%20or%20.tsx%20file%20extensions%20can%20be%20used%20for%20Pages. return ( - normalizedAbsoluteResourcePath.startsWith(appDirPath) && + normalizedAbsoluteResourcePath.startsWith(appDirPath + path.sep) && !!normalizedAbsoluteResourcePath.match(/[\\/](page|layout|loading|head|not-found)\.(js|jsx|tsx)$/) ); }, diff --git a/packages/nextjs/test/integration/test/server/utils/helpers.ts b/packages/nextjs/test/integration/test/server/utils/helpers.ts index c7213785493f..efc8c144eee2 100644 --- a/packages/nextjs/test/integration/test/server/utils/helpers.ts +++ b/packages/nextjs/test/integration/test/server/utils/helpers.ts @@ -1,10 +1,10 @@ -import { getPortPromise } from 'portfinder'; import { TestEnv } from '../../../../../../node-integration-tests/utils'; import * as http from 'http'; import * as path from 'path'; import { createServer, Server } from 'http'; import { parse } from 'url'; import next from 'next'; +import { AddressInfo } from 'net'; // Type not exported from NextJS // @ts-ignore @@ -24,9 +24,10 @@ export const createNextServer = async config => { }); }; -export const startServer = async (server: Server, port: string | number) => { - return new Promise(resolve => { - server.listen(port || 0, () => { +export const startServer = async (server: Server) => { + return new Promise<{ server: http.Server; url: string }>(resolve => { + server.listen(0, () => { + const port = (server.address() as AddressInfo).port; const url = `http://localhost:${port}`; resolve({ server, url }); }); @@ -39,7 +40,6 @@ export class NextTestEnv extends TestEnv { } public static async init(): Promise { - const port = await getPortPromise(); const server = await createNextServer({ dev: false, dir: path.resolve(__dirname, '../../..'), @@ -50,8 +50,8 @@ export class NextTestEnv extends TestEnv { conf: path.resolve(__dirname, '../../next.config.js'), }); - await startServer(server, port); + const { url } = await startServer(server); - return new NextTestEnv(server, `http://localhost:${port}`); + return new NextTestEnv(server, url); } } diff --git a/packages/node-integration-tests/package.json b/packages/node-integration-tests/package.json index 4d43ca35883d..5e7127f1adb5 100644 --- a/packages/node-integration-tests/package.json +++ b/packages/node-integration-tests/package.json @@ -31,6 +31,7 @@ "cors": "^2.8.5", "express": "^4.17.3", "graphql": "^16.3.0", + "http-terminator": "^3.2.0", "mongodb": "^3.7.3", "mongodb-memory-server-global": "^7.6.3", "mysql": "^2.18.1", diff --git a/packages/node-integration-tests/utils/index.ts b/packages/node-integration-tests/utils/index.ts index 88120853ee69..3bca893c34b3 100644 --- a/packages/node-integration-tests/utils/index.ts +++ b/packages/node-integration-tests/utils/index.ts @@ -6,10 +6,14 @@ import type { AxiosRequestConfig } from 'axios'; import axios from 'axios'; import type { Express } from 'express'; import type * as http from 'http'; +import type { HttpTerminator } from 'http-terminator'; +import { createHttpTerminator } from 'http-terminator'; import type { AddressInfo } from 'net'; import nock from 'nock'; import * as path from 'path'; +const NODE_VERSION = parseSemver(process.versions.node).major; + export type TestServerConfig = { url: string; server: http.Server; @@ -40,7 +44,6 @@ export type DataCollectorOptions = { * @return {*} {jest.Describe} */ export const conditionalTest = (allowedVersion: { min?: number; max?: number }): jest.Describe => { - const NODE_VERSION = parseSemver(process.versions.node).major; if (!NODE_VERSION) { return describe.skip; } @@ -123,10 +126,12 @@ async function makeRequest( export class TestEnv { private _axiosConfig: AxiosRequestConfig | undefined = undefined; + private _terminator: HttpTerminator; public constructor(public readonly server: http.Server, public readonly url: string) { this.server = server; this.url = url; + this._terminator = createHttpTerminator({ server: this.server, gracefulTerminationTimeout: 0 }); } /** @@ -244,12 +249,20 @@ export class TestEnv { // Ex: Remix scope bleed tests. nock.cleanAll(); - this.server.close(() => { - resolve(envelopes); - }); + // Abort all pending requests to nock to prevent hanging / flakes. + // See: https://github.com/nock/nock/issues/1118#issuecomment-544126948 + nock.abortPendingRequests(); + + this._closeServer() + .catch(e => { + logger.warn(e); + }) + .finally(() => { + resolve(envelopes); + }); + } else { + resolve(envelopes); } - - resolve(envelopes); } return true; @@ -291,12 +304,14 @@ export class TestEnv { nock.cleanAll(); - this.server.close(() => { + void this._closeServer().then(() => { resolve(reqCount); }); - - resolve(reqCount); }, options.timeout || 1000); }); } + + private _closeServer(): Promise { + return this._terminator.terminate(); + } } diff --git a/packages/remix/package.json b/packages/remix/package.json index 58bca014a549..c5eb4a5e9cda 100644 --- a/packages/remix/package.json +++ b/packages/remix/package.json @@ -39,8 +39,7 @@ "devDependencies": { "@remix-run/node": "^1.4.3", "@remix-run/react": "^1.4.3", - "@types/express": "^4.17.14", - "portfinder": "^1.0.28" + "@types/express": "^4.17.14" }, "peerDependencies": { "@remix-run/node": "1.x", diff --git a/packages/remix/test/integration/test/server/action.test.ts b/packages/remix/test/integration/test/server/action.test.ts index a2a4632ba962..edf94f20e253 100644 --- a/packages/remix/test/integration/test/server/action.test.ts +++ b/packages/remix/test/integration/test/server/action.test.ts @@ -38,7 +38,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada cookies: expect.any(Object), headers: { 'user-agent': expect.any(String), - host: 'localhost:8000', + host: expect.stringContaining('localhost:'), }, }, }); @@ -114,7 +114,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada cookies: expect.any(Object), headers: { 'user-agent': expect.any(String), - host: 'localhost:8000', + host: expect.stringContaining('localhost:'), }, }, }); @@ -134,7 +134,7 @@ describe.each(['builtin', 'express'])('Remix API Actions with adapter = %s', ada cookies: expect.any(Object), headers: { 'user-agent': expect.any(String), - host: 'localhost:8000', + host: expect.stringContaining('localhost:'), }, }, }); diff --git a/packages/remix/test/integration/test/server/utils/helpers.ts b/packages/remix/test/integration/test/server/utils/helpers.ts index a2cfedca0f94..7042940d6c14 100644 --- a/packages/remix/test/integration/test/server/utils/helpers.ts +++ b/packages/remix/test/integration/test/server/utils/helpers.ts @@ -1,9 +1,9 @@ import express from 'express'; import { createRequestHandler } from '@remix-run/express'; -import { getPortPromise } from 'portfinder'; import { wrapExpressCreateRequestHandler } from '@sentry/remix'; import { TestEnv } from '../../../../../../node-integration-tests/utils'; import * as http from 'http'; +import { AddressInfo } from 'net'; export * from '../../../../../../node-integration-tests/utils'; @@ -16,18 +16,18 @@ export class RemixTestEnv extends TestEnv { const requestHandlerFactory = adapter === 'express' ? wrapExpressCreateRequestHandler(createRequestHandler) : createRequestHandler; - const port = await getPortPromise(); - + let serverPort; const server = await new Promise(resolve => { const app = express(); app.all('*', requestHandlerFactory({ build: require('../../../build') })); - const server = app.listen(port, () => { + const server = app.listen(0, () => { + serverPort = (server.address() as AddressInfo).port; resolve(server); }); }); - return new RemixTestEnv(server, `http://localhost:${port}`); + return new RemixTestEnv(server, `http://localhost:${serverPort}`); } } diff --git a/packages/replay/src/coreHandlers/handleDom.ts b/packages/replay/src/coreHandlers/handleDom.ts index 71fb211ee3fe..e97e3fe5b8fe 100644 --- a/packages/replay/src/coreHandlers/handleDom.ts +++ b/packages/replay/src/coreHandlers/handleDom.ts @@ -27,8 +27,16 @@ export const handleDomListener: (replay: ReplayContainer) => (handlerData: DomHa const isClick = handlerData.name === 'click'; const event = isClick && (handlerData.event as PointerEvent); - // Ignore clicks if ctrl/alt/meta keys are held down as they alter behavior of clicks (e.g. open in new tab) - if (isClick && replay.clickDetector && event && !event.altKey && !event.metaKey && !event.ctrlKey) { + // Ignore clicks if ctrl/alt/meta/shift keys are held down as they alter behavior of clicks (e.g. open in new tab) + if ( + isClick && + replay.clickDetector && + event && + !event.altKey && + !event.metaKey && + !event.ctrlKey && + !event.shiftKey + ) { handleClick( replay.clickDetector, result as Breadcrumb & { timestamp: number; data: { nodeId: number } }, diff --git a/packages/replay/src/replay.ts b/packages/replay/src/replay.ts index 9ca3077e914f..cf0e72ffcc34 100644 --- a/packages/replay/src/replay.ts +++ b/packages/replay/src/replay.ts @@ -426,6 +426,10 @@ export class ReplayContainer implements ReplayContainerInterface { const activityTime = Date.now(); + // eslint-disable-next-line no-console + const log = this.getOptions()._experiments.traceInternals ? console.info : logger.info; + __DEBUG_BUILD__ && log(`[Replay] Converting buffer to session, starting at ${activityTime}`); + // Allow flush to complete before resuming as a session recording, otherwise // the checkout from `startRecording` may be included in the payload. // Prefer to keep the error replay as a separate (and smaller) segment @@ -981,9 +985,6 @@ export class ReplayContainer implements ReplayContainerInterface { const earliestEvent = eventBuffer.getEarliestTimestamp(); if (earliestEvent && earliestEvent < this._context.initialTimestamp) { - // eslint-disable-next-line no-console - const log = this.getOptions()._experiments.traceInternals ? console.info : logger.info; - __DEBUG_BUILD__ && log(`[Replay] Updating initial timestamp to ${earliestEvent}`); this._context.initialTimestamp = earliestEvent; } } @@ -1103,7 +1104,7 @@ export class ReplayContainer implements ReplayContainerInterface { return; } - const start = this._context.initialTimestamp; + const start = this.session.started; const now = Date.now(); const duration = now - start; diff --git a/packages/replay/src/util/handleRecordingEmit.ts b/packages/replay/src/util/handleRecordingEmit.ts index 987f589412ed..4039fad5caa8 100644 --- a/packages/replay/src/util/handleRecordingEmit.ts +++ b/packages/replay/src/util/handleRecordingEmit.ts @@ -72,6 +72,11 @@ export function getHandleRecordingEmit(replay: ReplayContainer): RecordingEmitCa if (replay.recordingMode === 'buffer' && replay.session && replay.eventBuffer) { const earliestEvent = replay.eventBuffer.getEarliestTimestamp(); if (earliestEvent) { + // eslint-disable-next-line no-console + const log = replay.getOptions()._experiments.traceInternals ? console.info : logger.info; + __DEBUG_BUILD__ && + log(`[Replay] Updating session start time to earliest event in buffer at ${earliestEvent}`); + replay.session.started = earliestEvent; if (replay.getOptions().stickySession) { diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts index 3475a3dbd894..3641993149bf 100644 --- a/packages/sveltekit/src/server/index.ts +++ b/packages/sveltekit/src/server/index.ts @@ -1,5 +1,5 @@ // Node SDK exports -// Unfortunately, we cannot `exprt * from '@sentry/node'` because in prod builds, +// Unfortunately, we cannot `export * from '@sentry/node'` because in prod builds, // Vite puts these exports into a `default` property (Sentry.default) rather than // on the top - level namespace. // Hence, we export everything from the Node SDK explicitly: diff --git a/yarn.lock b/yarn.lock index 197a789d7ceb..6619037a8e38 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6104,7 +6104,7 @@ ajv@8.6.2: require-from-string "^2.0.2" uri-js "^4.2.2" -ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.11.0, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -7853,6 +7853,11 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +boolean@^3.1.4: + version "3.2.0" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" + integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== + bower-config@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/bower-config/-/bower-config-1.4.3.tgz#3454fecdc5f08e7aa9cc6d556e492be0669689ae" @@ -10798,6 +10803,11 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" +delay@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" + integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -13129,6 +13139,16 @@ fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@2.x, fast-json-stab resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-json-stringify@^2.7.10: + version "2.7.13" + resolved "https://registry.yarnpkg.com/fast-json-stringify/-/fast-json-stringify-2.7.13.tgz#277aa86c2acba4d9851bd6108ed657aa327ed8c0" + integrity sha512-ar+hQ4+OIurUGjSJD1anvYSDcUflywhKjfxnsW4TBTD7+u0tJufv6DKRWoQk3vI6YBOWMoz0TQtfbe7dxbQmvA== + dependencies: + ajv "^6.11.0" + deepmerge "^4.2.2" + rfdc "^1.2.0" + string-similarity "^4.0.1" + fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -13141,6 +13161,13 @@ fast-ordered-set@^1.0.0: dependencies: blank-object "^1.0.1" +fast-printf@^1.6.9: + version "1.6.9" + resolved "https://registry.yarnpkg.com/fast-printf/-/fast-printf-1.6.9.tgz#212f56570d2dc8ccdd057ee93d50dd414d07d676" + integrity sha512-FChq8hbz65WMj4rstcQsFB0O7Cy++nmbNfLYnD9cYv2cRn8EG6k/MGn9kO/tjO66t09DLDugj3yL+V2o6Qftrg== + dependencies: + boolean "^3.1.4" + fast-sourcemap-concat@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-sourcemap-concat/-/fast-sourcemap-concat-2.1.0.tgz#12dd36bfc38c804093e4bd1de61dd6216f574211" @@ -13671,15 +13698,6 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-extra@11.1.0, fs-extra@^11.1.0: - version "11.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" - integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-extra@4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b" @@ -13708,6 +13726,15 @@ fs-extra@^10.0.0, fs-extra@^10.1.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" + integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.1.tgz#da69f7c39f3b002378b0954bb6ae7efdc0876e2d" @@ -14323,6 +14350,13 @@ globals@^9.18.0: resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== +globalthis@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + globalyzer@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/globalyzer/-/globalyzer-0.1.0.tgz#cb76da79555669a1519d5a8edf093afaa0bf1465" @@ -15104,6 +15138,16 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http-terminator@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/http-terminator/-/http-terminator-3.2.0.tgz#bc158d2694b733ca4fbf22a35065a81a609fb3e9" + integrity sha512-JLjck1EzPaWjsmIf8bziM3p9fgR1Y3JoUKAkyYEbZmFrIvJM6I8vVJfBGWlEtV9IWOvzNnaTtjuwZeBY2kwB4g== + dependencies: + delay "^5.0.0" + p-wait-for "^3.2.0" + roarr "^7.0.4" + type-fest "^2.3.3" + https-browserify@1.0.0, https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -20607,7 +20651,7 @@ p-timeout@^2.0.1: dependencies: p-finally "^1.0.0" -p-timeout@^3.1.0, p-timeout@^3.2.0: +p-timeout@^3.0.0, p-timeout@^3.1.0, p-timeout@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== @@ -20629,6 +20673,13 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +p-wait-for@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-wait-for/-/p-wait-for-3.2.0.tgz#640429bcabf3b0dd9f492c31539c5718cb6a3f1f" + integrity sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA== + dependencies: + p-timeout "^3.0.0" + p-waterfall@2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/p-waterfall/-/p-waterfall-2.1.1.tgz#63153a774f472ccdc4eb281cdb2967fcf158b2ee" @@ -21277,7 +21328,7 @@ pnp-webpack-plugin@1.6.4, pnp-webpack-plugin@^1.6.4: dependencies: ts-pnp "^1.1.6" -portfinder@^1.0.26, portfinder@^1.0.28, portfinder@^1.0.29: +portfinder@^1.0.26, portfinder@^1.0.29: version "1.0.32" resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg== @@ -23753,7 +23804,7 @@ rework@1.0.1: convert-source-map "^0.3.3" css "^2.0.0" -rfdc@^1.1.4, rfdc@^1.3.0: +rfdc@^1.1.4, rfdc@^1.2.0, rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== @@ -23811,6 +23862,18 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +roarr@^7.0.4: + version "7.15.0" + resolved "https://registry.yarnpkg.com/roarr/-/roarr-7.15.0.tgz#09b792f0cd31b4a7f91030bb1c47550ceec98ee4" + integrity sha512-CV9WefQfUXTX6wr8CrEMhfNef3sjIt9wNhE/5PNu4tNWsaoDNDXqq+OGn/RW9A1UPb0qc7FQlswXRaJJJsqn8A== + dependencies: + boolean "^3.1.4" + fast-json-stringify "^2.7.10" + fast-printf "^1.6.9" + globalthis "^1.0.2" + safe-stable-stringify "^2.4.1" + semver-compare "^1.0.0" + rollup-plugin-cleanup@3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/rollup-plugin-cleanup/-/rollup-plugin-cleanup-3.2.1.tgz#8cbc92ecf58babd7c210051929797f137bbf777c" @@ -23999,6 +24062,11 @@ safe-stable-stringify@^2.3.1: resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.1.tgz#34694bd8a30575b7f94792aa51527551bd733d61" integrity sha512-dVHE6bMtS/bnL2mwualjc6IxEv1F+OCUpA46pKUj6F8uDbUM0jCCulPqRNPSnWwGNKx5etqMjZYdXtrm5KJZGA== +safe-stable-stringify@^2.4.1: + version "2.4.3" + resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" + integrity sha512-e2bDA2WJT0wxseVd4lsDP4+3ONX6HpMXQa1ZhFQ7SU+GjvORCmShbCMltrtIDfkYhVHrOcPtj+KhmDBdPdZD1g== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -24182,6 +24250,11 @@ selfsigned@^1.10.7, selfsigned@^1.10.8: dependencies: node-forge "^0.10.0" +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== + semver-diff@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" @@ -25268,6 +25341,11 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-similarity@^4.0.1: + version "4.0.4" + resolved "https://registry.yarnpkg.com/string-similarity/-/string-similarity-4.0.4.tgz#42d01ab0b34660ea8a018da8f56a3309bb8b2a5b" + integrity sha512-/q/8Q4Bl4ZKAPjj8WerIBJWALKkaPRfrvhfF8k/B23i4nzrlRj2/go1m90In7nG/3XDSbOo0+pu6RvCTM9RGMQ== + string-template@~0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add" @@ -26598,6 +26676,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^2.3.3: + version "2.19.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" + integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"