From 15980a66d4bdbcd5e9f3494bcaa1a286f6ec9aee Mon Sep 17 00:00:00 2001 From: Tobias Koppers Date: Wed, 6 Sep 2023 19:46:54 +0200 Subject: [PATCH] remove --turbo, use --experimental-turbo as --turbo (#55063) ### What? Switch the default for `--turbo` to the new `--experimental-turbo`, remove the old code in next.js ### Why? The new approach will be used in future Closes WEB-1506 --- .github/workflows/build_and_test.yml | 42 +- jest.config.js | 7 +- .../next-swc/crates/napi/src/turbopack.rs | 7 - .../next-dev-tests/print-enabled-tests.js | 30 - .../crates/next-dev-tests/tests-manifest.js | 214 ---- packages/next/src/build/swc/index.ts | 157 +-- packages/next/src/cli/next-build.ts | 2 +- packages/next/src/cli/next-dev.ts | 197 +--- packages/next/src/server/lib/router-server.ts | 2 +- scripts/normalize-version-bump.js | 8 - .../ReactRefreshLogBox-builtins.test.ts | 163 ++- .../acceptance-app/ReactRefreshLogBox.test.ts | 1017 ++++++++--------- .../acceptance-app/error-recovery.test.ts | 503 ++++---- .../ReactRefreshLogBox-app-doc.test.ts | 183 ++- .../ReactRefreshLogBox-builtins.test.ts | 163 ++- .../acceptance/ReactRefreshLogBox.test.ts | 831 +++++++------- .../acceptance/error-recovery.test.ts | 435 ++++--- test/lib/e2e-utils.ts | 9 +- test/lib/next-test-utils.ts | 37 +- test/lib/turbo.ts | 28 - test/turbopack-tests-manifest.js | 212 ++++ 21 files changed, 1939 insertions(+), 2308 deletions(-) delete mode 100644 packages/next-swc/crates/next-dev-tests/print-enabled-tests.js delete mode 100644 packages/next-swc/crates/next-dev-tests/tests-manifest.js diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 98e113b4af1c9..696817a754302 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -124,37 +124,13 @@ jobs: afterBuild: turbo run rust-check secrets: inherit - test-experimental-turbopack-dev: - name: test experimental turbopack dev - needs: ['build-native', 'build-next'] - uses: ./.github/workflows/build_reusable.yml - with: - skipForDocsOnly: 'yes' - afterBuild: RUST_BACKTRACE=0 NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/test/turbopack-tests-manifest.js" EXPERIMENTAL_TURBOPACK=1 NEXT_E2E_TEST_TIMEOUT=240000 NEXT_TEST_MODE=dev node run-tests.js --test-pattern '^(test\/development)/.*\.test\.(js|jsx|ts|tsx)$' --timings -c ${TEST_CONCURRENCY} - secrets: inherit - test-turbopack-dev: name: test turbopack dev needs: ['build-native', 'build-next'] uses: ./.github/workflows/build_reusable.yml with: skipForDocsOnly: 'yes' - afterBuild: RUST_BACKTRACE=0 NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/packages/next-swc/crates/next-dev-tests/tests-manifest.js" TURBOPACK=1 __INTERNAL_CUSTOM_TURBOPACK_BINDINGS="$(pwd)/packages/next-swc/native/next-swc.linux-x64-gnu.node" NEXT_E2E_TEST_TIMEOUT=240000 NEXT_TEST_MODE=dev node run-tests.js --test-pattern '^(test\/development)/.*\.test\.(js|jsx|ts|tsx)$' --timings -c ${TEST_CONCURRENCY} - secrets: inherit - - test-experimental-turbopack-integration: - name: test experimental turbopack integration - needs: ['build-native', 'build-next'] - strategy: - fail-fast: false - matrix: - group: [1] - - uses: ./.github/workflows/build_reusable.yml - with: - nodeVersion: 16 - skipForDocsOnly: 'yes' - afterBuild: RUST_BACKTRACE=0 NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/test/turbopack-tests-manifest.js" EXPERIMENTAL_TURBOPACK=1 node run-tests.js --timings -g ${{ matrix.group }}/1 -c ${TEST_CONCURRENCY} --type integration + afterBuild: RUST_BACKTRACE=0 NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/test/turbopack-tests-manifest.js" TURBOPACK=1 NEXT_E2E_TEST_TIMEOUT=240000 NEXT_TEST_MODE=dev node run-tests.js --test-pattern '^(test\/development)/.*\.test\.(js|jsx|ts|tsx)$' --timings -c ${TEST_CONCURRENCY} secrets: inherit test-turbopack-integration: @@ -163,14 +139,15 @@ jobs: strategy: fail-fast: false matrix: - group: [1, 2, 3, 4, 5] + group: [1] uses: ./.github/workflows/build_reusable.yml with: nodeVersion: 16 skipForDocsOnly: 'yes' - afterBuild: RUST_BACKTRACE=0 NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/packages/next-swc/crates/next-dev-tests/tests-manifest.js" TURBOPACK=1 __INTERNAL_CUSTOM_TURBOPACK_BINDINGS="$(pwd)/packages/next-swc/native/next-swc.linux-x64-gnu.node" node run-tests.js --timings -g ${{ matrix.group }}/5 -c ${TEST_CONCURRENCY} --type integration + afterBuild: RUST_BACKTRACE=0 NEXT_EXTERNAL_TESTS_FILTERS="$(pwd)/test/turbopack-tests-manifest.js" TURBOPACK=1 node run-tests.js --timings -g ${{ matrix.group }}/1 -c ${TEST_CONCURRENCY} --type integration secrets: inherit + test-next-swc-wasm: name: test next-swc wasm needs: ['build-native', 'build-next'] @@ -235,14 +212,7 @@ jobs: secrets: inherit report-test-results: - needs: - [ - 'test-dev', - 'test-prod', - 'test-integration', - 'test-turbopack-dev', - 'test-turbopack-integration', - ] + needs: ['test-dev', 'test-prod', 'test-integration'] uses: ./.github/workflows/build_reusable.yml with: skipForDocsOnly: 'yes' @@ -266,9 +236,7 @@ jobs: 'rust-check', 'test-next-swc-wasm', 'test-turbopack-dev', - 'test-experimental-turbopack-dev', 'test-turbopack-integration', - 'test-experimental-turbopack-integration', ] if: always() diff --git a/jest.config.js b/jest.config.js index 347231fe3bf1a..96c993dd8be03 100644 --- a/jest.config.js +++ b/jest.config.js @@ -37,10 +37,9 @@ if (shouldEnableTestTrace) { customJestConfig.reporters = ['default'] } - const outputDirectory = - process.env.TURBOPACK || process.env.EXPERIMENTAL_TURBOPACK - ? '/turbopack-test-junit-report' - : '/test-junit-report' + const outputDirectory = process.env.TURBOPACK + ? '/turbopack-test-junit-report' + : '/test-junit-report' customJestConfig.reporters.push([ 'jest-junit', diff --git a/packages/next-swc/crates/napi/src/turbopack.rs b/packages/next-swc/crates/napi/src/turbopack.rs index 56046e394b0e7..984f56c46c932 100644 --- a/packages/next-swc/crates/napi/src/turbopack.rs +++ b/packages/next-swc/crates/napi/src/turbopack.rs @@ -9,16 +9,9 @@ use next_build::{ build as turbo_next_build, build_options::BuildContext, BuildOptions as NextBuildOptions, }; use next_core::next_config::{Rewrite, Rewrites, RouteHas}; -use next_dev::{devserver_options::DevServerOptions, start_server}; use crate::util::MapErr; -#[napi] -pub async fn start_turbo_dev(options: Buffer) -> napi::Result<()> { - let options: DevServerOptions = serde_json::from_slice(&options)?; - start_server(&options).await.convert_err() -} - #[napi(object, object_to_js = false)] #[derive(Debug)] pub struct NextBuildContext { diff --git a/packages/next-swc/crates/next-dev-tests/print-enabled-tests.js b/packages/next-swc/crates/next-dev-tests/print-enabled-tests.js deleted file mode 100644 index f122001a6e4f5..0000000000000 --- a/packages/next-swc/crates/next-dev-tests/print-enabled-tests.js +++ /dev/null @@ -1,30 +0,0 @@ -/// A script that prints a list of test cases that are enabled - -const _glob = require('glob') -const { promisify } = require('util') -const glob = promisify(_glob) -const path = require('path') - -const { disabledTests } = require('./tests-manifest.js') - -const main = async () => { - // Collect all test files - const testFiles = new Set( - ( - await glob('**/*.test.{js,ts,tsx}', { - nodir: true, - cwd: path.resolve(__dirname, '../../../../test'), - }) - ).map((file) => `test/${file}`) - ) - - for (const testFile of disabledTests) { - testFiles.delete(testFile) - } - - for (const testFile of testFiles) { - console.log(testFile) - } -} - -main().catch((e) => console.error(e)) diff --git a/packages/next-swc/crates/next-dev-tests/tests-manifest.js b/packages/next-swc/crates/next-dev-tests/tests-manifest.js deleted file mode 100644 index c02aea7d92245..0000000000000 --- a/packages/next-swc/crates/next-dev-tests/tests-manifest.js +++ /dev/null @@ -1,214 +0,0 @@ -// Tests that are currently enabled with Turbopack in CI. -// Only tests that are actively testing against Turbopack should -// be enabled here -const enabledTests = [ - 'test/development/acceptance-app/ReactRefresh.test.ts', - 'test/development/acceptance-app/ReactRefreshLogBoxMisc.test.ts', - 'test/development/acceptance-app/ReactRefreshRequire.test.ts', - // 'test/development/acceptance-app/app-hmr-changes.test.ts', (FLAKY) - 'test/development/acceptance-app/dynamic-error.test.ts', - 'test/development/acceptance-app/version-staleness.test.ts', - 'test/development/acceptance/ReactRefreshLogBox-scss.test.ts', - 'test/development/acceptance/ReactRefreshLogBoxMisc.test.ts', - 'test/development/api-cors-with-rewrite/index.test.ts', - 'test/development/app-dir/multiple-compiles-single-route/multiple-compiles-single-route.test.ts', - // x-ref: below test is flakey and needs to be investigated further - // 'test/development/app-hmr/hmr.test.ts', - 'test/development/basic/define-class-fields.test.ts', - 'test/development/basic/emotion-swc.test.ts', - 'test/development/basic/legacy-decorators.test.ts', - 'test/development/basic/project-directory-rename.test.ts', - 'test/development/basic/tailwind-jit.test.ts', - 'test/development/basic/theme-ui.test.ts', - 'test/development/dotenv-default-expansion/index.test.ts', - 'test/development/jsconfig-path-reloading/index.test.ts', - 'test/development/middleware-warnings/index.test.ts', - 'test/development/project-directory-with-styled-jsx-suffix/index.test.ts', - 'test/development/repeated-dev-edits/repeated-dev-edits.test.ts', - 'test/development/tsconfig-path-reloading/index.test.ts', - 'test/e2e/app-dir/_allow-underscored-root-directory/_allow-underscored-root-directory.test.ts', - 'test/e2e/app-dir/actions/app-action-invalid.test.ts', - 'test/e2e/app-dir/actions/app-action-size-limit-invalid.test.ts', - 'test/e2e/app-dir/app-alias/app-alias.test.ts', - 'test/e2e/app-dir/app-client-cache/client-cache.test.ts', - 'test/e2e/app-dir/app-css-pageextensions/index.test.ts', - 'test/e2e/app-dir/app-edge-root-layout/index.test.ts', - 'test/e2e/app-dir/app-edge/app-edge.test.ts', - 'test/e2e/app-dir/app-prefetch-false/app-prefetch-false.test.ts', - 'test/e2e/app-dir/app-prefetch/prefetching.test.ts', - 'test/e2e/app-dir/app-validation/validation.test.ts', - 'test/e2e/app-dir/asset-prefix/asset-prefix.test.ts', - 'test/e2e/app-dir/async-component-preload/async-component-preload.test.ts', - 'test/e2e/app-dir/autoscroll-with-css-modules/index.test.ts', - 'test/e2e/app-dir/back-button-download-bug/back-button-download-bug.test.ts', - 'test/e2e/app-dir/build-size/index.test.ts', - 'test/e2e/app-dir/global-error/global-error.test.ts', - 'test/e2e/app-dir/hello-world/hello-world.test.ts', - 'test/e2e/app-dir/import/import.test.ts', - 'test/e2e/app-dir/layout-params/layout-params.test.ts', - 'test/e2e/app-dir/metadata-suspense/index.test.ts', - 'test/e2e/app-dir/rewrites-redirects/rewrites-redirects.test.ts', - 'test/e2e/app-dir/route-page-manifest-bug/route-page-manifest-bug.test.ts', - 'test/e2e/app-dir/router-autoscroll/router-autoscroll.test.ts', - 'test/e2e/app-dir/router-stuck-dynamic-static-segment/router-stuck-dynamic-static-segment.test.ts', - 'test/e2e/app-dir/rsc-basic/rsc-basic.test.ts', - 'test/e2e/app-dir/search-params-react-key/layout-params.test.ts', - 'test/e2e/app-dir/searchparams-static-bailout/searchparams-static-bailout.test.ts', - 'test/e2e/app-dir/test-template/{{ toFileName name }}/{{ toFileName name }}.test.ts', - 'test/e2e/app-dir/use-selected-layout-segment-s/use-selected-layout-segment-s.test.ts', - 'test/e2e/app-dir/crypto-globally-available/crypto-globally-available.test.ts', - 'test/e2e/browserslist-extends/index.test.ts', - 'test/e2e/config-promise-export/async-function.test.ts', - 'test/e2e/config-promise-export/promise.test.ts', - 'test/e2e/conflicting-app-page-error/index.test.ts', - 'test/e2e/disable-js-preload/test/index.test.js', - 'test/e2e/hello-world/hello-world.test.ts', - 'test/e2e/i18n-api-support/index.test.ts', - 'test/e2e/i18n-disallow-multiple-locales/i18n-disallow-multiple-locales.test.ts', - 'test/e2e/i18n-ignore-rewrite-source-locale/rewrites-with-basepath.test.ts', - 'test/e2e/i18n-ignore-rewrite-source-locale/rewrites.test.ts', - 'test/e2e/link-with-api-rewrite/index.test.ts', - 'test/e2e/middleware-fetches-with-body/index.test.ts', - 'test/e2e/middleware-shallow-link/index.test.ts', - 'test/e2e/new-link-behavior/child-a-tag-error.test.ts', - 'test/e2e/new-link-behavior/index.test.ts', - 'test/e2e/new-link-behavior/material-ui.test.ts', - 'test/e2e/new-link-behavior/stitches.test.ts', - 'test/e2e/new-link-behavior/typescript.test.ts', - 'test/e2e/next-head/index.test.ts', - 'test/e2e/next-image-forward-ref/index.test.ts', - 'test/e2e/no-eslint-warn-with-no-eslint-config/index.test.ts', - 'test/e2e/nonce-head-manager/index.test.ts', - 'test/e2e/postcss-config-cjs/index.test.ts', - 'test/e2e/proxy-request-with-middleware/test/index.test.ts', - 'test/e2e/repeated-forward-slashes-error/repeated-forward-slashes-error.test.ts', - 'test/e2e/styled-jsx/index.test.ts', - 'test/e2e/test-template/{{ toFileName name }}/{{ toFileName name }}.test.ts', - 'test/e2e/test-utils-tests/basic/basic.test.ts', - 'test/e2e/trailingslash-with-rewrite/index.test.ts', - 'test/e2e/transpile-packages/index.test.ts', - 'test/e2e/type-module-interop/index.test.ts', - 'test/e2e/typescript-version-no-warning/typescript-version-no-warning.test.ts', - 'test/e2e/typescript-version-warning/typescript-version-warning.test.ts', - 'test/e2e/undici-fetch/index.test.ts', - 'test/e2e/yarn-pnp/test/with-eslint.test.ts', - 'test/e2e/yarn-pnp/test/with-next-sass.test.ts', - 'test/integration/404-page-custom-error/test/index.test.js', - 'test/integration/amp-export-validation/test/index.test.js', - 'test/integration/amphtml-custom-validator/test/index.test.js', - 'test/integration/amphtml-fragment-style/test/index.test.js', - 'test/integration/api-body-parser/test/index.test.js', - 'test/integration/api-catch-all/test/index.test.js', - 'test/integration/app-aspath/test/index.test.js', - 'test/integration/app-config-asset-prefix/test/index.test.js', - 'test/integration/app-functional/test/index.test.js', - 'test/integration/auto-export-error-bail/test/index.test.js', - 'test/integration/auto-export-query-error/test/index.test.js', - 'test/integration/bigint/test/index.test.js', - 'test/integration/catches-missing-getStaticProps/test/index.test.js', - 'test/integration/clean-distdir/test/index.test.js', - 'test/integration/client-navigation-a11y/test/index.test.js', - // TODO: re-enable once the logging is aligned - // 'test/integration/config-experimental-warning/test/index.test.js', - 'test/integration/config-schema-check/test/index.test.js', - 'test/integration/config-syntax-error/test/index.test.js', - 'test/integration/config-validation/test/index.test.ts', - 'test/integration/conflicting-ssg-paths/test/index.test.js', - 'test/integration/create-next-app/index.test.ts', - 'test/integration/create-next-app/package-manager.test.ts', - 'test/integration/create-next-app/templates-app.test.ts', - 'test/integration/create-next-app/templates-pages.test.ts', - 'test/integration/css/test/dev-css-handling.test.js', - 'test/integration/custom-error-page-exception/test/index.test.js', - 'test/integration/custom-server-types/test/index.test.js', - 'test/integration/custom-server/test/index.test.js', - 'test/integration/dedupes-scripts/test/index.test.js', - 'test/integration/development-hmr-refresh/test/index.test.js', - 'test/integration/disable-js/test/index.test.js', - 'test/integration/document-head-warnings/test/index.test.js', - 'test/integration/duplicate-pages/test/index.test.js', - 'test/integration/dynamic-require/test/index.test.js', - 'test/integration/dynamic-route-rename/test/index.test.js', - 'test/integration/empty-object-getInitialProps/test/index.test.js', - 'test/integration/errors-on-output-to-public/test/index.test.js', - 'test/integration/errors-on-output-to-static/test/index.test.js', - 'test/integration/eslint/test/lint-cache.test.js', - 'test/integration/eslint/test/next-lint.test.js', - 'test/integration/export-404/test/index.test.js', - 'test/integration/export-default-map/test/index.test.js', - 'test/integration/export-dynamic-pages/test/index.test.js', - 'test/integration/export-fallback-true-error/test/index.test.js', - 'test/integration/export-getInitialProps-warn/test/index.test.js', - 'test/integration/export-image-default/test/index.test.js', - 'test/integration/export-image-loader-legacy/test/index.test.js', - 'test/integration/export-index-not-found-gsp/test/index.test.ts', - 'test/integration/export-intent/test/index.test.js', - 'test/integration/export-no-build/test/index.test.js', - 'test/integration/export-progress-status-message/test/index.test.js', - 'test/integration/export-subfolders/test/index.test.js', - 'test/integration/fallback-modules/test/index.test.js', - 'test/integration/filesystempublicroutes/test/index.test.js', - 'test/integration/firebase-grpc/test/index.test.js', - 'test/integration/future/test/index.test.js', - 'test/integration/gsp-extension/test/index.test.js', - 'test/integration/handles-export-errors/test/index.test.js', - 'test/integration/hashbang/test/index.test.js', - 'test/integration/index-index/test/index.test.js', - 'test/integration/initial-ref/test/index.test.js', - 'test/integration/invalid-config-values/test/index.test.js', - 'test/integration/invalid-page-automatic-static-optimization/test/index.test.js', - 'test/integration/invalid-revalidate-values/test/index.test.js', - 'test/integration/invalid-server-options/test/index.test.js', - 'test/integration/json-serialize-original-error/test/index.test.js', - 'test/integration/legacy-ssg-methods-error/test/index.test.js', - 'test/integration/link-ref/test/index.test.js', - 'test/integration/link-with-multiple-child/test/index.test.js', - 'test/integration/link-without-router/test/index.test.js', - 'test/integration/middleware-build-errors/test/index.test.js', - 'test/integration/middleware-overrides-node.js-api/test/index.test.ts', - 'test/integration/missing-document-component-error/test/index.test.js', - 'test/integration/next-image-legacy/custom-resolver/test/index.test.ts', - 'test/integration/next-image-legacy/no-intersection-observer-fallback/test/index.test.ts', - 'test/integration/next-image-legacy/noscript/test/index.test.ts', - 'test/integration/next-image-legacy/react-virtualized/test/index.test.ts', - 'test/integration/next-image-legacy/unoptimized/test/index.test.ts', - 'test/integration/next-image-new/react-virtualized/test/index.test.ts', - // 'test/integration/next-image-new/unoptimized/test/index.test.ts', (FLAKY) - 'test/integration/no-op-export/test/index.test.js', - 'test/integration/non-next-dist-exclude/test/index.test.js', - 'test/integration/non-standard-node-env-warning/test/index.test.js', - 'test/integration/ondemand/test/index.test.js', - 'test/integration/optional-chaining-nullish-coalescing/test/index.test.js', - 'test/integration/plugin-mdx-rs/test/index.test.js', - 'test/integration/prerender-invalid-catchall-params/test/index.test.js', - 'test/integration/prerender-invalid-paths/test/index.test.js', - 'test/integration/prerender-legacy/test/index.test.js', - 'test/integration/production-build-dir/test/index.test.js', - 'test/integration/production-start-no-build/test/index.test.js', - 'test/integration/router-hash-navigation/test/index.test.js', - 'test/integration/router-prefetch/test/index.test.js', - 'test/integration/router-rerender/test/index.test.js', - 'test/integration/scss/test/dev-css-handling.test.js', - 'test/integration/src-dir-support-double-dir/test/index.test.js', - 'test/integration/ssg-dynamic-routes-404-page/test/index.test.js', - 'test/integration/static-404/test/index.test.js', - 'test/integration/static-page-name/test/index.test.js', - 'test/integration/trailing-slashes-href-resolving/test/index.test.js', - 'test/integration/tsconfig-verifier/test/index.test.js', - 'test/integration/typescript-baseurl/test/index.test.js', - 'test/integration/typescript-external-dir/project/test/index.test.js', - 'test/integration/typescript-filtered-files/test/index.test.js', - // 'test/integration/typescript-hmr/test/index.test.js', (FLAKY) - 'test/integration/webpack-config-mainjs/test/index.test.js', - 'test/integration/with-electron/test/index.test.js', - 'test/production/ci-missing-typescript-deps/index.test.ts', - 'test/production/enoent-during-require/index.test.ts', - 'test/production/eslint-plugin-deps/index.test.ts', - 'test/production/fallback-export-error/index.test.ts', - 'test/production/jest/new-link-behavior.test.ts', - 'test/production/jest/transpile-packages.test.ts', - 'test/production/postcss-plugin-config-as-string/index.test.ts', - 'test/production/supports-module-resolution-nodenext/supports-moduleresolution-nodenext.test.ts', -] - -module.exports = { enabledTests } diff --git a/packages/next/src/build/swc/index.ts b/packages/next/src/build/swc/index.ts index debf23fa7c3b1..28d205c81c234 100644 --- a/packages/next/src/build/swc/index.ts +++ b/packages/next/src/build/swc/index.ts @@ -8,7 +8,6 @@ import { getParserOptions } from './options' import { eventSwcLoadFailure } from '../../telemetry/events/swc-load-failure' import { patchIncorrectLockfile } from '../../lib/patch-incorrect-lockfile' import { downloadWasmSwc, downloadNativeNextSwc } from '../../lib/download-swc' -import { spawn } from 'child_process' import { NextConfigComplete, TurboLoaderItem } from '../../server/config-shared' import { isDeepStrictEqual } from 'util' @@ -93,23 +92,8 @@ const triples = (() => { // naive interface - `loadBindings` will not validate neither path nor the binary. // // Note these are internal flag: there's no stability, feature guarantee. -const __INTERNAL_CUSTOM_TURBOPACK_BINARY = - process.env.__INTERNAL_CUSTOM_TURBOPACK_BINARY const __INTERNAL_CUSTOM_TURBOPACK_BINDINGS = process.env.__INTERNAL_CUSTOM_TURBOPACK_BINDINGS -export const __isCustomTurbopackBinary = async (): Promise => { - if ( - !!__INTERNAL_CUSTOM_TURBOPACK_BINARY && - !!__INTERNAL_CUSTOM_TURBOPACK_BINDINGS - ) { - throw new Error('Cannot use TURBOPACK_BINARY and TURBOPACK_BINDINGS both') - } - - return ( - !!__INTERNAL_CUSTOM_TURBOPACK_BINARY || - !!__INTERNAL_CUSTOM_TURBOPACK_BINDINGS - ) -} function checkVersionMismatch(pkgData: any) { const version = pkgData.version @@ -158,7 +142,6 @@ export const lockfilePatchPromise: { cur?: Promise } = {} export interface Binding { isWasm: boolean turbo: { - startDev: any startTrace: any nextBuild?: any createTurboTasks?: any @@ -208,7 +191,6 @@ export async function loadBindings(): Promise { } } - const isCustomTurbopack = await __isCustomTurbopackBinary() pendingBindings = new Promise(async (resolve, _reject) => { if (!lockfilePatchPromise.cur) { // always run lockfile check once so that it gets patched @@ -229,10 +211,7 @@ export async function loadBindings(): Promise { if (shouldLoadWasmFallbackFirst) { lastNativeBindingsLoadErrorCode = 'unsupported_target' - const fallbackBindings = await tryLoadWasmWithFallback( - attempts, - isCustomTurbopack - ) + const fallbackBindings = await tryLoadWasmWithFallback(attempts) if (fallbackBindings) { return resolve(fallbackBindings) } @@ -247,16 +226,13 @@ export async function loadBindings(): Promise { // with other reasons than `ERR_MODULE_NOT_FOUND`. // - Lastly, falls back to wasm binding where possible. try { - return resolve(loadNative(isCustomTurbopack)) + return resolve(loadNative()) } catch (a) { if ( Array.isArray(a) && a.every((m) => m.includes('it was not installed')) ) { - let fallbackBindings = await tryLoadNativeWithFallback( - attempts, - isCustomTurbopack - ) + let fallbackBindings = await tryLoadNativeWithFallback(attempts) if (fallbackBindings) { return resolve(fallbackBindings) @@ -268,10 +244,7 @@ export async function loadBindings(): Promise { // For these platforms we already tried to load wasm and failed, skip reattempt if (!shouldLoadWasmFallbackFirst && !disableWasmFallback) { - const fallbackBindings = await tryLoadWasmWithFallback( - attempts, - isCustomTurbopack - ) + const fallbackBindings = await tryLoadWasmWithFallback(attempts) if (fallbackBindings) { return resolve(fallbackBindings) } @@ -282,10 +255,7 @@ export async function loadBindings(): Promise { return pendingBindings } -async function tryLoadNativeWithFallback( - attempts: Array, - isCustomTurbopack: boolean -) { +async function tryLoadNativeWithFallback(attempts: Array) { const nativeBindingsDirectory = path.join( path.dirname(require.resolve('next/package.json')), 'next-swc-fallback' @@ -301,7 +271,7 @@ async function tryLoadNativeWithFallback( await downloadNativeBindingsPromise try { - let bindings = loadNative(isCustomTurbopack, nativeBindingsDirectory) + let bindings = loadNative(nativeBindingsDirectory) return bindings } catch (a: any) { attempts.concat(a) @@ -309,12 +279,9 @@ async function tryLoadNativeWithFallback( return undefined } -async function tryLoadWasmWithFallback( - attempts: any, - isCustomTurbopack: boolean -) { +async function tryLoadWasmWithFallback(attempts: any) { try { - let bindings = await loadWasm('', isCustomTurbopack) + let bindings = await loadWasm('') // @ts-expect-error TODO: this event has a wrong type. eventSwcLoadFailure({ wasm: 'enabled', @@ -338,10 +305,7 @@ async function tryLoadWasmWithFallback( downloadWasmPromise = downloadWasmSwc(nextVersion, wasmDirectory) } await downloadWasmPromise - let bindings = await loadWasm( - pathToFileURL(wasmDirectory).href, - isCustomTurbopack - ) + let bindings = await loadWasm(pathToFileURL(wasmDirectory).href) // @ts-expect-error TODO: this event has a wrong type. eventSwcLoadFailure({ wasm: 'fallback', @@ -1041,7 +1005,7 @@ function bindingToApi(binding: any, _wasm: boolean) { return createProject } -async function loadWasm(importPath = '', isCustomTurbopack: boolean) { +async function loadWasm(importPath = '') { if (wasmBindings) { return wasmBindings } @@ -1095,27 +1059,6 @@ async function loadWasm(importPath = '', isCustomTurbopack: boolean) { return undefined }, turbo: { - startDev: (options: any) => { - if (!isCustomTurbopack) { - Log.error('Wasm binding does not support --turbo yet') - return - } else if (!!__INTERNAL_CUSTOM_TURBOPACK_BINDINGS) { - Log.warn( - 'Trying to load custom turbopack bindings. Note this is internal testing purpose only, actual wasm fallback cannot load this bindings' - ) - Log.warn( - `Loading custom turbopack bindings from ${__INTERNAL_CUSTOM_TURBOPACK_BINDINGS}` - ) - - const devOptions = { - ...options, - noOpen: options.noOpen ?? true, - } - require(__INTERNAL_CUSTOM_TURBOPACK_BINDINGS).startTurboDev( - toBuffer(devOptions) - ) - } - }, startTrace: () => { Log.error('Wasm binding does not support trace yet') }, @@ -1175,7 +1118,7 @@ async function loadWasm(importPath = '', isCustomTurbopack: boolean) { throw attempts } -function loadNative(isCustomTurbopack = false, importPath?: string) { +function loadNative(importPath?: string) { if (nativeBindings) { return nativeBindings } @@ -1298,84 +1241,6 @@ function loadNative(isCustomTurbopack = false, importPath?: string) { teardownHeapProfiler: bindings.teardownHeapProfiler, teardownCrashReporter: bindings.teardownCrashReporter, turbo: { - startDev: (options: any) => { - initHeapProfiler() - - const devOptions = { - ...options, - noOpen: options.noOpen ?? true, - } - - if (!isCustomTurbopack) { - bindings.startTurboDev(toBuffer(devOptions)) - } else if (!!__INTERNAL_CUSTOM_TURBOPACK_BINARY) { - console.warn( - `Loading custom turbopack binary from ${__INTERNAL_CUSTOM_TURBOPACK_BINARY}` - ) - - return new Promise((resolve, reject) => { - const args: any[] = [] - - Object.entries(devOptions).forEach(([key, value]) => { - let cli_key = `--${key.replace( - /[A-Z]/g, - (m) => '-' + m.toLowerCase() - )}` - if (key === 'dir') { - args.push(value) - } else if (typeof value === 'boolean' && value === true) { - args.push(cli_key) - } else if (typeof value !== 'boolean' && !!value) { - args.push(cli_key, value) - } - }) - - console.warn(`Running turbopack with args: [${args.join(' ')}]`) - - let child = spawn(__INTERNAL_CUSTOM_TURBOPACK_BINARY, args, { - stdio: 'pipe', - }) - child.on('message', (message: any) => { - require('console').log(message) - }) - child.stdout.on('data', (data: any) => { - require('console').log(data.toString()) - }) - child.stderr.on('data', (data: any) => { - require('console').log(data.toString()) - }) - - child.on('close', (code: any) => { - if (code !== 0) { - reject({ - command: `${__INTERNAL_CUSTOM_TURBOPACK_BINARY} ${args.join( - ' ' - )}`, - }) - return - } - resolve(0) - }) - - process.on('beforeExit', () => { - if (child) { - console.log('Killing turbopack process') - child.kill() - child = null as any - } - }) - }) - } else if (!!__INTERNAL_CUSTOM_TURBOPACK_BINDINGS) { - console.warn( - `Loading custom turbopack bindings from ${__INTERNAL_CUSTOM_TURBOPACK_BINDINGS}` - ) - console.warn(`Running turbopack with args: `, devOptions) - - require(__INTERNAL_CUSTOM_TURBOPACK_BINDINGS).startTurboDev( - toBuffer(devOptions) - ) - } - }, nextBuild: (options: unknown) => { initHeapProfiler() const ret = (customBindings ?? bindings).nextBuild(options) diff --git a/packages/next/src/cli/next-build.ts b/packages/next/src/cli/next-build.ts index c20762c828908..fc7decbe032f3 100755 --- a/packages/next/src/cli/next-build.ts +++ b/packages/next/src/cli/next-build.ts @@ -69,7 +69,7 @@ const nextBuild: CliCommand = (argv) => { printAndExit(`> No such directory exists as the project root: ${dir}`) } - if (args['--experimental-turbo'] || process.env.EXPERIMENTAL_TURBOPACK) { + if (args['--experimental-turbo']) { process.env.TURBOPACK = '1' } diff --git a/packages/next/src/cli/next-dev.ts b/packages/next/src/cli/next-dev.ts index 8b618894b99fb..17792914bbc8c 100644 --- a/packages/next/src/cli/next-dev.ts +++ b/packages/next/src/cli/next-dev.ts @@ -16,11 +16,10 @@ import { setGlobal, traceGlobals } from '../trace/shared' import { Telemetry } from '../telemetry/storage' import loadConfig, { getEnabledExperimentalFeatures } from '../server/config' import { findPagesDir } from '../lib/find-pages-dir' -import { findRootDir } from '../lib/find-root' import { fileExists, FileType } from '../lib/file-exists' import { getNpxCommand } from '../lib/helpers/get-npx-command' import Watchpack from 'watchpack' -import { resetEnv, initialEnv } from '@next/env' +import { initialEnv } from '@next/env' import { getValidatedArgs } from '../lib/get-validated-args' import { Worker } from 'next/dist/compiled/jest-worker' import type { ChildProcess } from 'child_process' @@ -144,7 +143,7 @@ async function createRouterWorker(fullConfig: NextConfigComplete): Promise<{ ? { __NEXT_PRIVATE_CPU_PROFILE: `CPU.router` } : {}), WATCHPACK_WATCHER_LIMIT: '20', - EXPERIMENTAL_TURBOPACK: process.env.EXPERIMENTAL_TURBOPACK, + TURBOPACK: process.env.TURBOPACK, __NEXT_PRIVATE_PREBUNDLED_REACT: !!fullConfig.experimental.serverActions ? 'experimental' : 'next', @@ -348,161 +347,83 @@ const nextDev: CliCommand = async (argv) => { if (args['--turbo']) { process.env.TURBOPACK = '1' } - if (args['--experimental-turbo']) { - process.env.EXPERIMENTAL_TURBOPACK = '1' - } const distDir = path.join(dir, config.distDir ?? '.next') setGlobal('phase', PHASE_DEVELOPMENT_SERVER) setGlobal('distDir', distDir) - if (process.env.TURBOPACK) { - isTurboSession = true - - const { validateTurboNextConfig } = - require('../lib/turbopack-warning') as typeof import('../lib/turbopack-warning') - const { loadBindings, __isCustomTurbopackBinary, teardownHeapProfiler } = - require('../build/swc') as typeof import('../build/swc') - const { eventCliSession } = - require('../telemetry/events/version') as typeof import('../telemetry/events/version') - require('../telemetry/storage') as typeof import('../telemetry/storage') - const findUp = - require('next/dist/compiled/find-up') as typeof import('next/dist/compiled/find-up') - - const isCustomTurbopack = await __isCustomTurbopackBinary() - const rawNextConfig = await validateTurboNextConfig({ - isCustomTurbopack, - ...devServerOptions, - isDev: true, - }) - - const { pagesDir, appDir } = findPagesDir(dir) - const telemetry = new Telemetry({ - distDir, - }) - setGlobal('appDir', appDir) - setGlobal('pagesDir', pagesDir) - setGlobal('telemetry', telemetry) - - if (!isCustomTurbopack) { - telemetry.record( - eventCliSession(distDir, rawNextConfig as NextConfigComplete, { - webpackVersion: 5, - cliCommand: 'dev', - isSrcDir: path - .relative(dir, pagesDir || appDir || '') - .startsWith('src'), - hasNowJson: !!(await findUp('now.json', { cwd: dir })), - isCustomServer: false, - turboFlag: true, - pagesDir: !!pagesDir, - appDir: !!appDir, - }) - ) - } - - // Turbopack need to be in control over reading the .env files and watching them. - // So we need to start with a initial env to know which env vars are coming from the user. - resetEnv() - let server - trace('start-dev-server').traceAsyncFn(async (_) => { - let bindings = await loadBindings() - - server = bindings.turbo.startDev({ - ...devServerOptions, - showAll: args['--show-all'] ?? false, - root: args['--root'] ?? findRootDir(dir), - }) + const runDevServer = async (reboot: boolean) => { + try { + const workerInit = await createRouterWorker(config) - // Start preflight after server is listening and ignore errors: - preflight(false).catch(() => {}) - }) - - if (!isCustomTurbopack) { - await telemetry.flush() - } + if (!!args['--experimental-https']) { + Log.warn( + 'Self-signed certificates are currently an experimental feature, use at your own risk.' + ) - // There are some cases like test fixtures teardown that normal flush won't hit. - // Force flush those on those case, but don't wait for it. - ;['SIGTERM', 'SIGINT', 'beforeExit', 'exit'].forEach((event) => - process.on(event, () => teardownHeapProfiler()) - ) + let certificate: { key: string; cert: string } | undefined - return server - } else { - const runDevServer = async (reboot: boolean) => { - try { - const workerInit = await createRouterWorker(config) - - if (!!args['--experimental-https']) { - Log.warn( - 'Self-signed certificates are currently an experimental feature, use at your own risk.' - ) - - let certificate: { key: string; cert: string } | undefined - - if ( - args['--experimental-https-key'] && - args['--experimental-https-cert'] - ) { - certificate = { - key: path.resolve(args['--experimental-https-key']), - cert: path.resolve(args['--experimental-https-cert']), - } - } else { - certificate = await createSelfSignedCertificate(host) + if ( + args['--experimental-https-key'] && + args['--experimental-https-cert'] + ) { + certificate = { + key: path.resolve(args['--experimental-https-key']), + cert: path.resolve(args['--experimental-https-cert']), } - - await workerInit.worker.startServer({ - ...devServerOptions, - selfSignedCertificate: certificate, - }) } else { - await workerInit.worker.startServer(devServerOptions) + certificate = await createSelfSignedCertificate(host) } - await preflight(reboot) - return { - cleanup: workerInit.cleanup, - } - } catch (err) { - console.error(err) - process.exit(1) + await workerInit.worker.startServer({ + ...devServerOptions, + selfSignedCertificate: certificate, + }) + } else { + await workerInit.worker.startServer(devServerOptions) + } + + await preflight(reboot) + return { + cleanup: workerInit.cleanup, } + } catch (err) { + console.error(err) + process.exit(1) } + } - let runningServer: Awaited> | undefined + let runningServer: Awaited> | undefined - watchConfigFiles(devServerOptions.dir, async (filename) => { - if (process.env.__NEXT_DISABLE_MEMORY_WATCHER) { - Log.info( - `Detected change, manual restart required due to '__NEXT_DISABLE_MEMORY_WATCHER' usage` - ) - return - } - // Adding a new line to avoid the logs going directly after the spinner in `next build` - Log.warn('') - Log.warn( - `Found a change in ${path.basename( - filename - )}. Restarting the server to apply the changes...` + watchConfigFiles(devServerOptions.dir, async (filename) => { + if (process.env.__NEXT_DISABLE_MEMORY_WATCHER) { + Log.info( + `Detected change, manual restart required due to '__NEXT_DISABLE_MEMORY_WATCHER' usage` ) + return + } + // Adding a new line to avoid the logs going directly after the spinner in `next build` + Log.warn('') + Log.warn( + `Found a change in ${path.basename( + filename + )}. Restarting the server to apply the changes...` + ) - try { - if (runningServer) { - await runningServer.cleanup() - } - runningServer = await runDevServer(true) - } catch (err) { - console.error(err) - process.exit(1) + try { + if (runningServer) { + await runningServer.cleanup() } - }) + runningServer = await runDevServer(true) + } catch (err) { + console.error(err) + process.exit(1) + } + }) - await trace('start-dev-server').traceAsyncFn(async (_) => { - runningServer = await runDevServer(false) - }) - } + await trace('start-dev-server').traceAsyncFn(async (_) => { + runningServer = await runDevServer(false) + }) } export { nextDev } diff --git a/packages/next/src/server/lib/router-server.ts b/packages/next/src/server/lib/router-server.ts index 567862c845e22..90d23955195c1 100644 --- a/packages/next/src/server/lib/router-server.ts +++ b/packages/next/src/server/lib/router-server.ts @@ -118,7 +118,7 @@ export async function initialize(opts: { dir: opts.dir, nextConfig: config, isCustomServer: opts.customServer, - turbo: !!process.env.EXPERIMENTAL_TURBOPACK, + turbo: !!process.env.TURBOPACK, }) } diff --git a/scripts/normalize-version-bump.js b/scripts/normalize-version-bump.js index 890606dd9a30f..e13eb4b8928fb 100755 --- a/scripts/normalize-version-bump.js +++ b/scripts/normalize-version-bump.js @@ -20,14 +20,6 @@ const writeJson = async (filePath, data) => ;(async function () { const packages = await fs.readdir(path.join(cwd, 'packages')) - await fs.unlink( - path.join( - cwd, - 'packages', - 'next-swc/crates/next-dev-tests/tests-manifest.js' - ) - ) - const pkgJsonData = new Map() const pkgNames = [] await Promise.all( diff --git a/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts b/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts index 3f0d4fb250dc0..2bfdd40e93c3e 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox-builtins.test.ts @@ -5,64 +5,62 @@ import { describeVariants as describe } from 'next-test-utils' import { outdent } from 'outdent' // TODO-APP: Investigate snapshot mismatch -describe.each(['default', 'turbo', 'experimentalTurbo'])( - 'ReactRefreshLogBox app %s', - () => { - const { next } = nextTestSetup({ - files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), - dependencies: { - react: 'latest', - 'react-dom': 'latest', - }, - skipStart: true, - }) - - // Module trace is only available with webpack 5 - test('Node.js builtins', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'node_modules/my-package/index.js', - outdent` +describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => { + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + dependencies: { + react: 'latest', + 'react-dom': 'latest', + }, + skipStart: true, + }) + + // Module trace is only available with webpack 5 + test('Node.js builtins', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'node_modules/my-package/index.js', + outdent` const dns = require('dns') module.exports = dns `, - ], - [ - 'node_modules/my-package/package.json', - outdent` + ], + [ + 'node_modules/my-package/package.json', + outdent` { "name": "my-package", "version": "0.0.1" } `, - ], - ]) - ) + ], + ]) + ) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import pkg from 'my-package' export default function Hello() { return (pkg ?

Package loaded

:

Package did not load

) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Module not found', async () => { - const { session, cleanup } = await sandbox(next) + test('Module not found', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import Comp from 'b' export default function Oops() { return ( @@ -72,22 +70,22 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Module not found empty import trace', async () => { - const { session, cleanup } = await sandbox(next) + test('Module not found empty import trace', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'app/page.js', - outdent` + await session.patch( + 'app/page.js', + outdent` 'use client' import Comp from 'b' export default function Oops() { @@ -98,52 +96,51 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Module not found missing global CSS', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'app/page.js', - outdent` + test('Module not found missing global CSS', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'app/page.js', + outdent` 'use client' import './non-existent.css' export default function Page(props) { return

index page

} `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await session.patch( - 'app/page.js', - outdent` + await session.patch( + 'app/page.js', + outdent` 'use client' export default function Page(props) { return

index page

} ` - ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.documentElement.innerHTML) - ).toContain('index page') - - await cleanup() - }) - } -) + ) + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.documentElement.innerHTML) + ).toContain('index page') + + await cleanup() + }) +}) diff --git a/test/development/acceptance-app/ReactRefreshLogBox.test.ts b/test/development/acceptance-app/ReactRefreshLogBox.test.ts index ba9c732e6cb59..ba71baa42f42f 100644 --- a/test/development/acceptance-app/ReactRefreshLogBox.test.ts +++ b/test/development/acceptance-app/ReactRefreshLogBox.test.ts @@ -5,24 +5,22 @@ import { check, describeVariants as describe } from 'next-test-utils' import path from 'path' import { outdent } from 'outdent' -describe.each(['default', 'turbo', 'experimentalTurbo'])( - 'ReactRefreshLogBox app %s', - () => { - const { next } = nextTestSetup({ - files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), - dependencies: { - react: 'latest', - 'react-dom': 'latest', - }, - skipStart: true, - }) - - test('should strip whitespace correctly with newline', async () => { - const { session, cleanup } = await sandbox(next) - - await session.patch( - 'index.js', - outdent` +describe.each(['default', 'turbo'])('ReactRefreshLogBox app %s', () => { + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + dependencies: { + react: 'latest', + 'react-dom': 'latest', + }, + skipStart: true, + }) + + test('should strip whitespace correctly with newline', async () => { + const { session, cleanup } = await sandbox(next) + + await session.patch( + 'index.js', + outdent` export default function Page() { return ( <> @@ -38,24 +36,24 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - await session.evaluate(() => document.querySelector('a').click()) + ) + await session.evaluate(() => document.querySelector('a').click()) - await session.waitForAndOpenRuntimeError() - expect(await session.getRedboxSource()).toMatchSnapshot() + await session.waitForAndOpenRuntimeError() + expect(await session.getRedboxSource()).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137807 - test('module init error not shown', async () => { - // Start here: - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137807 + test('module init error not shown', async () => { + // Start here: + const { session, cleanup } = await sandbox(next) - // We start here. - await session.patch( - 'index.js', - outdent` + // We start here. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { render() { @@ -64,16 +62,16 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } export default ClassDefault; ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('h1').textContent) - ).toBe('Default Export') + expect( + await session.evaluate(() => document.querySelector('h1').textContent) + ).toBe('Default Export') - // Add a throw in module init phase: - await session.patch( - 'index.js', - outdent` + // Add a throw in module init phase: + await session.patch( + 'index.js', + outdent` // top offset for snapshot import * as React from 'react'; throw new Error('no') @@ -84,33 +82,33 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } export default ClassDefault; ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - if (process.platform === 'win32') { - expect(await session.getRedboxSource()).toMatchSnapshot() - } else { - expect(await session.getRedboxSource()).toMatchSnapshot() - } + expect(await session.hasRedbox(true)).toBe(true) + if (process.platform === 'win32') { + expect(await session.getRedboxSource()).toMatchSnapshot() + } else { + expect(await session.getRedboxSource()).toMatchSnapshot() + } - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554152127 - test('boundaries', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554152127 + test('boundaries', async () => { + const { session, cleanup } = await sandbox(next) - await session.write( - 'FunctionDefault.js', - outdent` + await session.write( + 'FunctionDefault.js', + outdent` export default function FunctionDefault() { return

hello

} ` - ) - await session.patch( - 'index.js', - outdent` + ) + await session.patch( + 'index.js', + outdent` import FunctionDefault from './FunctionDefault.js' import * as React from 'react' class ErrorBoundary extends React.Component { @@ -140,59 +138,59 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } export default App; ` - ) - - expect( - await session.evaluate(() => document.querySelector('h2').textContent) - ).toBe('hello') - - await session.write( - 'FunctionDefault.js', - `export default function FunctionDefault() { throw new Error('no'); }` - ) - - await session.waitForAndOpenRuntimeError() - expect(await session.getRedboxSource()).toMatchSnapshot() - expect( - await session.evaluate(() => document.querySelector('h2').textContent) - ).toBe('error') + ) - await cleanup() - }) + expect( + await session.evaluate(() => document.querySelector('h2').textContent) + ).toBe('hello') - // TODO: investigate why this fails when running outside of the Next.js - // monorepo e.g. fails when using yarn create next-app - // https://github.com/vercel/next.js/pull/23203 - test.skip('internal package errors', async () => { - const { session, cleanup } = await sandbox(next) + await session.write( + 'FunctionDefault.js', + `export default function FunctionDefault() { throw new Error('no'); }` + ) - // Make a react build-time error. - await session.patch( - 'index.js', - outdent` + await session.waitForAndOpenRuntimeError() + expect(await session.getRedboxSource()).toMatchSnapshot() + expect( + await session.evaluate(() => document.querySelector('h2').textContent) + ).toBe('error') + + await cleanup() + }) + + // TODO: investigate why this fails when running outside of the Next.js + // monorepo e.g. fails when using yarn create next-app + // https://github.com/vercel/next.js/pull/23203 + test.skip('internal package errors', async () => { + const { session, cleanup } = await sandbox(next) + + // Make a react build-time error. + await session.patch( + 'index.js', + outdent` export default function FunctionNamed() { return
{{}}
} ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - // We internally only check the script path, not including the line number - // and error message because the error comes from an external library. - // This test ensures that the errored script path is correctly resolved. - expect(await session.getRedboxSource()).toContain( - `../../../../packages/next/dist/pages/_document.js` - ) + expect(await session.hasRedbox(true)).toBe(true) + // We internally only check the script path, not including the line number + // and error message because the error comes from an external library. + // This test ensures that the errored script path is correctly resolved. + expect(await session.getRedboxSource()).toContain( + `../../../../packages/next/dist/pages/_document.js` + ) - await cleanup() - }) + await cleanup() + }) - test('unterminated JSX', async () => { - const { session, cleanup } = await sandbox(next) + test('unterminated JSX', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` export default () => { return (
@@ -201,13 +199,13 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(false)).toBe(false) + expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` export default () => { return (
@@ -216,13 +214,13 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(next.normalizeTestDirContent(source)).toMatchInlineSnapshot( - next.normalizeSnapshot(` + const source = await session.getRedboxSource() + expect(next.normalizeTestDirContent(source)).toMatchInlineSnapshot( + next.normalizeSnapshot(` "./index.js Error: x Unexpected token. Did you mean \`{'}'}\` or \`}\`? @@ -249,27 +247,27 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ./index.js ./app/page.js" `) - ) + ) - await cleanup() - }) + await cleanup() + }) - // Module trace is only available with webpack 5 - test('conversion to class component (1)', async () => { - const { session, cleanup } = await sandbox(next) + // Module trace is only available with webpack 5 + test('conversion to class component (1)', async () => { + const { session, cleanup } = await sandbox(next) - await session.write( - 'Child.js', - outdent` + await session.write( + 'Child.js', + outdent` export default function ClickCount() { return

hello

} ` - ) + ) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import Child from './Child'; export default function Home() { @@ -280,16 +278,16 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('hello') + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('hello') - await session.patch( - 'Child.js', - outdent` + await session.patch( + 'Child.js', + outdent` import { Component } from 'react'; export default class ClickCount extends Component { render() { @@ -297,14 +295,14 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - await session.patch( - 'Child.js', - outdent` + await session.patch( + 'Child.js', + outdent` import { Component } from 'react'; export default class ClickCount extends Component { render() { @@ -312,23 +310,23 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } } ` - ) + ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('hello new') + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('hello new') - await cleanup() - }) + await cleanup() + }) - test('css syntax errors', async () => { - const { session, cleanup } = await sandbox(next) + test('css syntax errors', async () => { + const { session, cleanup } = await sandbox(next) - await session.write('index.module.css', `.button {}`) - await session.patch( - 'index.js', - outdent` + await session.write('index.module.css', `.button {}`) + await session.patch( + 'index.js', + outdent` import './index.module.css'; export default () => { return ( @@ -338,35 +336,35 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - - expect(await session.hasRedbox(false)).toBe(false) - - // Syntax error - await session.patch('index.module.css', `.button {`) - expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatch('./index.module.css (1:1)') - expect(source).toMatch('Syntax error: ') - expect(source).toMatch('Unclosed block') - expect(source).toMatch('> 1 | .button {') - expect(source).toMatch(' | ^') - - // Not local error - await session.patch('index.module.css', `button {}`) - expect(await session.hasRedbox(true)).toBe(true) - const source2 = await session.getRedboxSource() - expect(source2).toMatchSnapshot() - - await cleanup() - }) - - test('logbox: anchors links in error messages', async () => { - const { session, cleanup } = await sandbox(next) + ) - await session.patch( - 'index.js', - outdent` + expect(await session.hasRedbox(false)).toBe(false) + + // Syntax error + await session.patch('index.module.css', `.button {`) + expect(await session.hasRedbox(true)).toBe(true) + const source = await session.getRedboxSource() + expect(source).toMatch('./index.module.css (1:1)') + expect(source).toMatch('Syntax error: ') + expect(source).toMatch('Unclosed block') + expect(source).toMatch('> 1 | .button {') + expect(source).toMatch(' | ^') + + // Not local error + await session.patch('index.module.css', `button {}`) + expect(await session.hasRedbox(true)).toBe(true) + const source2 = await session.getRedboxSource() + expect(source2).toMatchSnapshot() + + await cleanup() + }) + + test('logbox: anchors links in error messages', async () => { + const { session, cleanup } = await sandbox(next) + + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -380,38 +378,38 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - - await session.evaluate(() => document.querySelector('button').click()) - await session.waitForAndOpenRuntimeError() + ) - const header = await session.getRedboxDescription() - expect(header).toMatchSnapshot() - expect( - await session.evaluate( - () => + await session.evaluate(() => document.querySelector('button').click()) + await session.waitForAndOpenRuntimeError() + + const header = await session.getRedboxDescription() + expect(header).toMatchSnapshot() + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(1) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(1) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -425,38 +423,38 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - - await session.evaluate(() => document.querySelector('button').click()) - await session.waitForAndOpenRuntimeError() + ) - const header2 = await session.getRedboxDescription() - expect(header2).toMatchSnapshot() - expect( - await session.evaluate( - () => + await session.evaluate(() => document.querySelector('button').click()) + await session.waitForAndOpenRuntimeError() + + const header2 = await session.getRedboxDescription() + expect(header2).toMatchSnapshot() + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(1) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(1) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -470,38 +468,38 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - - await session.evaluate(() => document.querySelector('button').click()) - await session.waitForAndOpenRuntimeError() + ) - const header3 = await session.getRedboxDescription() - expect(header3).toMatchSnapshot() - expect( - await session.evaluate( - () => + await session.evaluate(() => document.querySelector('button').click()) + await session.waitForAndOpenRuntimeError() + + const header3 = await session.getRedboxDescription() + expect(header3).toMatchSnapshot() + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(1) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(1) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -515,59 +513,59 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - await session.evaluate(() => document.querySelector('button').click()) - await session.waitForAndOpenRuntimeError() + await session.evaluate(() => document.querySelector('button').click()) + await session.waitForAndOpenRuntimeError() - const header4 = await session.getRedboxDescription() - expect(header4).toMatchInlineSnapshot( - `"Error: multiple http://nextjs.org links http://example.com"` - ) - expect( - await session.evaluate( - () => + const header4 = await session.getRedboxDescription() + expect(header4).toMatchInlineSnapshot( + `"Error: multiple http://nextjs.org links http://example.com"` + ) + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(2) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(2) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(2)' - ) as any - ).href - ) - ).toMatchSnapshot() + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() + expect( + await session.evaluate( + () => + ( + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(2)' + ) as any + ).href + ) + ).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - // TODO-APP: Catch errors that happen before useEffect - test.skip('non-Error errors are handled properly', async () => { - const { session, cleanup } = await sandbox(next) + // TODO-APP: Catch errors that happen before useEffect + test.skip('non-Error errors are handled properly', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` export default () => { throw {'a': 1, 'b': 'x'}; return ( @@ -575,28 +573,28 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: {\\"a\\":1,\\"b\\":\\"x\\"}"` - ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: {\\"a\\":1,\\"b\\":\\"x\\"}"` + ) - // fix previous error - await session.patch( - 'index.js', - outdent` + // fix previous error + await session.patch( + 'index.js', + outdent` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - outdent` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + outdent` class Hello {} export default () => { @@ -606,27 +604,27 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toContain( - `Error: class Hello {` - ) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toContain( + `Error: class Hello {` + ) - // fix previous error - await session.patch( - 'index.js', - outdent` + // fix previous error + await session.patch( + 'index.js', + outdent` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - outdent` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + outdent` export default () => { throw "string error" return ( @@ -634,27 +632,27 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: string error"` - ) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: string error"` + ) - // fix previous error - await session.patch( - 'index.js', - outdent` + // fix previous error + await session.patch( + 'index.js', + outdent` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - outdent` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + outdent` export default () => { throw null return ( @@ -662,21 +660,21 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toContain( - `Error: A null error was thrown` - ) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toContain( + `Error: A null error was thrown` + ) - await cleanup() - }) + await cleanup() + }) - test('Should not show __webpack_exports__ when exporting anonymous arrow function', async () => { - const { session, cleanup } = await sandbox(next) + test('Should not show __webpack_exports__ when exporting anonymous arrow function', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` export default () => { if (typeof window !== 'undefined') { throw new Error('test') @@ -685,18 +683,18 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( return null } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Unhandled errors and rejections opens up in the minimized state', async () => { - const { session, browser, cleanup } = await sandbox(next) + test('Unhandled errors and rejections opens up in the minimized state', async () => { + const { session, browser, cleanup } = await sandbox(next) - const file = outdent` + const file = outdent` export default function Index() { // setTimeout(() => { @@ -728,62 +726,62 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } ` - await session.patch('index.js', file) + await session.patch('index.js', file) - // Unhandled error and rejection in setTimeout - expect( - await browser.waitForElementByCss('.nextjs-toast-errors').text() - ).toBe('2 errors') + // Unhandled error and rejection in setTimeout + expect( + await browser.waitForElementByCss('.nextjs-toast-errors').text() + ).toBe('2 errors') - // Unhandled error in event handler - await browser.elementById('unhandled-error').click() - await check( - () => browser.elementByCss('.nextjs-toast-errors').text(), - /3 errors/ - ) + // Unhandled error in event handler + await browser.elementById('unhandled-error').click() + await check( + () => browser.elementByCss('.nextjs-toast-errors').text(), + /3 errors/ + ) - // Unhandled rejection in event handler - await browser.elementById('unhandled-rejection').click() - await check( - () => browser.elementByCss('.nextjs-toast-errors').text(), - /4 errors/ - ) - expect(await session.hasRedbox(false)).toBe(false) + // Unhandled rejection in event handler + await browser.elementById('unhandled-rejection').click() + await check( + () => browser.elementByCss('.nextjs-toast-errors').text(), + /4 errors/ + ) + expect(await session.hasRedbox(false)).toBe(false) - // Add Component error - await session.patch( - 'index.js', - file.replace( - '//', - "if (typeof window !== 'undefined') throw new Error('Component error')" - ) + // Add Component error + await session.patch( + 'index.js', + file.replace( + '//', + "if (typeof window !== 'undefined') throw new Error('Component error')" ) + ) - // Render error should "win" and show up in fullscreen - expect(await session.hasRedbox(true)).toBe(true) - - await cleanup() - }) - - test.each(['server', 'client'])( - 'Call stack count is correct for %s error', - async (pageType) => { - const fixture = - pageType === 'server' - ? new Map([ - [ - 'app/page.js', - outdent` + // Render error should "win" and show up in fullscreen + expect(await session.hasRedbox(true)).toBe(true) + + await cleanup() + }) + + test.each(['server', 'client'])( + 'Call stack count is correct for %s error', + async (pageType) => { + const fixture = + pageType === 'server' + ? new Map([ + [ + 'app/page.js', + outdent` export default function Page() { throw new Error('Server error') } `, - ], - ]) - : new Map([ - [ - 'app/page.js', - outdent` + ], + ]) + : new Map([ + [ + 'app/page.js', + outdent` 'use client' export default function Page() { if (typeof window !== 'undefined') { @@ -792,87 +790,87 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( return null } `, - ], - ]) - - const { session, browser, cleanup } = await sandbox(next, fixture) + ], + ]) - const getCallStackCount = async () => - (await browser.elementsByCss('[data-nextjs-call-stack-frame]')).length + const { session, browser, cleanup } = await sandbox(next, fixture) - expect(await session.hasRedbox(true)).toBe(true) + const getCallStackCount = async () => + (await browser.elementsByCss('[data-nextjs-call-stack-frame]')).length - // Open full Call Stack - await browser - .elementByCss('[data-nextjs-data-runtime-error-collapsed-action]') - .click() + expect(await session.hasRedbox(true)).toBe(true) - // Expect more than the default amount of frames - // The default stackTraceLimit results in max 9 [data-nextjs-call-stack-frame] elements - expect(await getCallStackCount()).toBeGreaterThan(9) + // Open full Call Stack + await browser + .elementByCss('[data-nextjs-data-runtime-error-collapsed-action]') + .click() - await cleanup() - } - ) + // Expect more than the default amount of frames + // The default stackTraceLimit results in max 9 [data-nextjs-call-stack-frame] elements + expect(await getCallStackCount()).toBeGreaterThan(9) - test('Server component errors should open up in fullscreen', async () => { - const { session, browser, cleanup } = await sandbox( - next, - new Map([ - // Start with error - [ - 'app/page.js', - outdent` + await cleanup() + } + ) + + test('Server component errors should open up in fullscreen', async () => { + const { session, browser, cleanup } = await sandbox( + next, + new Map([ + // Start with error + [ + 'app/page.js', + outdent` export default function Page() { throw new Error('Server component error') return

Hello world

} `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Remove error - await session.patch( - 'app/page.js', - outdent` + // Remove error + await session.patch( + 'app/page.js', + outdent` export default function Page() { return

Hello world

} ` - ) - expect(await browser.waitForElementByCss('#text').text()).toBe( - 'Hello world' - ) - expect(await session.hasRedbox(false)).toBe(false) + ) + expect(await browser.waitForElementByCss('#text').text()).toBe( + 'Hello world' + ) + expect(await session.hasRedbox(false)).toBe(false) - // Re-add error - await session.patch( - 'app/page.js', - outdent` + // Re-add error + await session.patch( + 'app/page.js', + outdent` export default function Page() { throw new Error('Server component error!') return

Hello world

} ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - await cleanup() - }) + await cleanup() + }) - test('Import trace when module not found in layout', async () => { - const { session, cleanup } = await sandbox( - next, + test('Import trace when module not found in layout', async () => { + const { session, cleanup } = await sandbox( + next, - new Map([['app/module.js', `import "non-existing-module"`]]) - ) + new Map([['app/module.js', `import "non-existing-module"`]]) + ) - await session.patch( - 'app/layout.js', - outdent` + await session.patch( + 'app/layout.js', + outdent` import "./module" export default function RootLayout({ children }) { @@ -884,26 +882,26 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - await cleanup() - }) - - test("Can't resolve @import in CSS file", async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - ['app/styles1.css', '@import "./styles2.css"'], - ['app/styles2.css', '@import "./boom.css"'], - ]) - ) + await cleanup() + }) - await session.patch( - 'app/layout.js', - outdent` + test("Can't resolve @import in CSS file", async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + ['app/styles1.css', '@import "./styles2.css"'], + ['app/styles2.css', '@import "./boom.css"'], + ]) + ) + + await session.patch( + 'app/layout.js', + outdent` import "./styles1.css" export default function RootLayout({ children }) { @@ -915,29 +913,28 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) + + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() + + await cleanup() + }) + test.each([['server'], ['client']])( + '%s component can recover from error thrown in the module', + async (type: string) => { + const { session, cleanup } = await sandbox(next, undefined, '/' + type) + + await next.patchFile('index.js', "throw new Error('module error')") expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + await next.patchFile( + 'index.js', + 'export default function Page() {return

hello world

}' + ) + expect(await session.hasRedbox(false)).toBe(false) await cleanup() - }) - - test.each([['server'], ['client']])( - '%s component can recover from error thrown in the module', - async (type: string) => { - const { session, cleanup } = await sandbox(next, undefined, '/' + type) - - await next.patchFile('index.js', "throw new Error('module error')") - expect(await session.hasRedbox(true)).toBe(true) - await next.patchFile( - 'index.js', - 'export default function Page() {return

hello world

}' - ) - expect(await session.hasRedbox(false)).toBe(false) - - await cleanup() - } - ) - } -) + } + ) +}) diff --git a/test/development/acceptance-app/error-recovery.test.ts b/test/development/acceptance-app/error-recovery.test.ts index 97af1e54c469c..5315ae453c2bf 100644 --- a/test/development/acceptance-app/error-recovery.test.ts +++ b/test/development/acceptance-app/error-recovery.test.ts @@ -5,24 +5,22 @@ import { check, describeVariants as describe } from 'next-test-utils' import path from 'path' import { outdent } from 'outdent' -describe.each(['default', 'turbo', 'experimentalTurbo'])( - 'Error recovery app %s', - () => { - const { next } = nextTestSetup({ - files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), - dependencies: { - react: 'latest', - 'react-dom': 'latest', - }, - skipStart: true, - }) - - test('can recover from a syntax error without losing state', async () => { - const { session, cleanup } = await sandbox(next) - - await session.patch( - 'index.js', - outdent` +describe.each(['default', 'turbo'])('Error recovery app %s', () => { + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + dependencies: { + react: 'latest', + 'react-dom': 'latest', + }, + skipStart: true, + }) + + test('can recover from a syntax error without losing state', async () => { + const { session, cleanup } = await sandbox(next) + + await session.patch( + 'index.js', + outdent` import { useCallback, useState } from 'react' export default function Index() { @@ -36,23 +34,23 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') - await session.patch('index.js', `export default () =>
session.evaluate(() => document.querySelector('p').textContent), + /Count: 1/ + ) - await check( - () => session.evaluate(() => document.querySelector('p').textContent), - /Count: 1/ + await cleanup() + }) + + test.each([['client'], ['server']])( + '%s component can recover from syntax error', + async (type: string) => { + const { session, browser, cleanup } = await sandbox( + next, + undefined, + '/' + type ) - await cleanup() - }) - - test.each([['client'], ['server']])( - '%s component can recover from syntax error', - async (type: string) => { - const { session, browser, cleanup } = await sandbox( - next, - undefined, - '/' + type - ) - - // Add syntax error - await session.patch( - `app/${type}/page.js`, - outdent` + // Add syntax error + await session.patch( + `app/${type}/page.js`, + outdent` export default function Page() { return

Hello world

` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Fix syntax error - await session.patch( - `app/${type}/page.js`, - outdent` + // Fix syntax error + await session.patch( + `app/${type}/page.js`, + outdent` export default function Page() { return

Hello world 2

} ` - ) + ) - await check(() => browser.elementByCss('p').text(), 'Hello world 2') - await cleanup() - } - ) + await check(() => browser.elementByCss('p').text(), 'Hello world 2') + await cleanup() + } + ) - test('can recover from a event handler error', async () => { - const { session, cleanup } = await sandbox(next) + test('can recover from a event handler error', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import { useCallback, useState } from 'react' export default function Index() { @@ -134,18 +132,18 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('0') - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('1') + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('0') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') - await session.waitForAndOpenRuntimeError() - expect(await session.getRedboxSource()).toMatchInlineSnapshot(` + await session.waitForAndOpenRuntimeError() + expect(await session.getRedboxSource()).toMatchInlineSnapshot(` "index.js (7:10) @ eval 5 | const increment = useCallback(() => { @@ -157,9 +155,9 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( 10 |
" `) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import { useCallback, useState } from 'react' export default function Index() { @@ -173,46 +171,46 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(false)).toBe(false) - expect(await session.hasErrorToast()).toBe(false) + expect(await session.hasRedbox(false)).toBe(false) + expect(await session.hasErrorToast()).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Count: 1') - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Count: 2') + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 2') - expect(await session.hasRedbox(false)).toBe(false) - expect(await session.hasErrorToast()).toBe(false) + expect(await session.hasRedbox(false)).toBe(false) + expect(await session.hasErrorToast()).toBe(false) - await cleanup() - }) - - test.each([['client'], ['server']])( - '%s component can recover from a component error', - async (type: string) => { - const { session, cleanup, browser } = await sandbox( - next, - undefined, - '/' + type - ) - - await session.write( - 'child.js', - outdent` + await cleanup() + }) + + test.each([['client'], ['server']])( + '%s component can recover from a component error', + async (type: string) => { + const { session, cleanup, browser } = await sandbox( + next, + undefined, + '/' + type + ) + + await session.write( + 'child.js', + outdent` export default function Child() { return

Hello

; } ` - ) + ) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import Child from './child' export default function Index() { @@ -223,65 +221,65 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await browser.elementByCss('p').text()).toBe('Hello') + expect(await browser.elementByCss('p').text()).toBe('Hello') - await session.patch( - 'child.js', - outdent` + await session.patch( + 'child.js', + outdent` // hello export default function Child() { throw new Error('oops') } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toInclude( - 'export default function Child()' - ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toInclude( + 'export default function Child()' + ) - // TODO-APP: re-enable when error recovery doesn't reload the page. - /* const didNotReload = */ await session.patch( - 'child.js', - outdent` + // TODO-APP: re-enable when error recovery doesn't reload the page. + /* const didNotReload = */ await session.patch( + 'child.js', + outdent` export default function Child() { return

Hello

; } ` - ) + ) - // TODO-APP: re-enable when error recovery doesn't reload the page. - // expect(didNotReload).toBe(true) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Hello') + // TODO-APP: re-enable when error recovery doesn't reload the page. + // expect(didNotReload).toBe(true) + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Hello') - await cleanup() - } - ) + await cleanup() + } + ) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554150098 - test('syntax > runtime error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554150098 + test('syntax > runtime error', async () => { + const { session, cleanup } = await sandbox(next) - // Start here. - await session.patch( - 'index.js', - outdent` + // Start here. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; export default function FunctionNamed() { return
} ` - ) - // TODO: this acts weird without above step - await session.patch( - 'index.js', - outdent` + ) + // TODO: this acts weird without above step + await session.patch( + 'index.js', + outdent` import * as React from 'react'; let i = 0 setInterval(() => { @@ -292,18 +290,18 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( return
} ` - ) + ) - await new Promise((resolve) => setTimeout(resolve, 1000)) - await session.waitForAndOpenRuntimeError() - expect(await session.getRedboxSource()).not.toInclude( - "Expected '}', got ''" - ) + await new Promise((resolve) => setTimeout(resolve, 1000)) + await session.waitForAndOpenRuntimeError() + expect(await session.getRedboxSource()).not.toInclude( + "Expected '}', got ''" + ) - // Make a syntax error. - await session.patch( - 'index.js', - outdent` + // Make a syntax error. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; let i = 0 setInterval(() => { @@ -312,32 +310,32 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( }, 1000) export default function FunctionNamed() { ` - ) + ) - await new Promise((resolve) => setTimeout(resolve, 1000)) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toInclude( - "Expected '}', got ''" - ) + await new Promise((resolve) => setTimeout(resolve, 1000)) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toInclude( + "Expected '}', got ''" + ) - // Test that runtime error does not take over: - await new Promise((resolve) => setTimeout(resolve, 2000)) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toInclude( - "Expected '}', got ''" - ) + // Test that runtime error does not take over: + await new Promise((resolve) => setTimeout(resolve, 2000)) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toInclude( + "Expected '}', got ''" + ) - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554144016 - test('stuck error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554144016 + test('stuck error', async () => { + const { session, cleanup } = await sandbox(next) - // We start here. - await session.patch( - 'index.js', - outdent` + // We start here. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; function FunctionDefault() { @@ -346,23 +344,23 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default FunctionDefault; ` - ) + ) - // We add a new file. Let's call it Foo.js. - await session.write( - 'Foo.js', - outdent` + // We add a new file. Let's call it Foo.js. + await session.write( + 'Foo.js', + outdent` // intentionally skips export export default function Foo() { return React.createElement('h1', null, 'Foo'); } ` - ) + ) - // We edit our first file to use it. - await session.patch( - 'index.js', - outdent` + // We edit our first file to use it. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; import Foo from './Foo'; function FunctionDefault() { @@ -370,39 +368,39 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } export default FunctionDefault; ` - ) + ) - // We get an error because Foo didn't import React. Fair. - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toInclude( - "return React.createElement('h1', null, 'Foo');" - ) + // We get an error because Foo didn't import React. Fair. + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toInclude( + "return React.createElement('h1', null, 'Foo');" + ) - // Let's add that to Foo. - await session.patch( - 'Foo.js', - outdent` + // Let's add that to Foo. + await session.patch( + 'Foo.js', + outdent` import * as React from 'react'; export default function Foo() { return React.createElement('h1', null, 'Foo'); } ` - ) + ) - // Expected: this fixes the problem - expect(await session.hasRedbox(false)).toBe(false) + // Expected: this fixes the problem + expect(await session.hasRedbox(false)).toBe(false) - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137262 - test('render error not shown right after syntax error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137262 + test('render error not shown right after syntax error', async () => { + const { session, cleanup } = await sandbox(next) - // Starting here: - await session.patch( - 'index.js', - outdent` + // Starting here: + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { render() { @@ -412,16 +410,16 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default ClassDefault; ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('h1').textContent) - ).toBe('Default Export') + expect( + await session.evaluate(() => document.querySelector('h1').textContent) + ).toBe('Default Export') - // Break it with a syntax error: - await session.patch( - 'index.js', - outdent` + // Break it with a syntax error: + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { @@ -432,13 +430,13 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Now change the code to introduce a runtime error without fixing the syntax error: - await session.patch( - 'index.js', - outdent` + // Now change the code to introduce a runtime error without fixing the syntax error: + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { @@ -450,13 +448,13 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Now fix the syntax error: - await session.patch( - 'index.js', - outdent` + // Now fix the syntax error: + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { @@ -468,31 +466,30 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - await check(async () => { - const source = await session.getRedboxSource() - return source?.includes('render() {') ? 'success' : source - }, 'success') + await check(async () => { + const source = await session.getRedboxSource() + return source?.includes('render() {') ? 'success' : source + }, 'success') - expect(await session.getRedboxSource()).toInclude( - "throw new Error('nooo');" - ) + expect(await session.getRedboxSource()).toInclude( + "throw new Error('nooo');" + ) - await cleanup() - }) + await cleanup() + }) - test('displays build error on initial page load', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([['app/page.js', '{{{']]) - ) + test('displays build error on initial page load', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([['app/page.js', '{{{']]) + ) - expect(await session.hasRedbox(true)).toBe(true) - await check(() => session.getRedboxSource(true), /Failed to compile/) + expect(await session.hasRedbox(true)).toBe(true) + await check(() => session.getRedboxSource(true), /Failed to compile/) - await cleanup() - }) - } -) + await cleanup() + }) +}) diff --git a/test/development/acceptance/ReactRefreshLogBox-app-doc.test.ts b/test/development/acceptance/ReactRefreshLogBox-app-doc.test.ts index 509d9ab7ed710..163bdf84ebf41 100644 --- a/test/development/acceptance/ReactRefreshLogBox-app-doc.test.ts +++ b/test/development/acceptance/ReactRefreshLogBox-app-doc.test.ts @@ -4,50 +4,48 @@ import { describeVariants as describe } from 'next-test-utils' import { outdent } from 'outdent' import path from 'path' -describe.each(['default', 'turbo', 'experimentalTurbo'])( - 'ReactRefreshLogBox %s', - () => { - const { next } = nextTestSetup({ - files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), - skipStart: true, - }) - - test('empty _app shows logbox', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([['pages/_app.js', ``]]) - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: The default export is not a React Component in page: \\"/_app\\""` - ) - - await session.patch( - 'pages/_app.js', - outdent` +describe.each(['default', 'turbo'])('ReactRefreshLogBox %s', () => { + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + skipStart: true, + }) + + test('empty _app shows logbox', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([['pages/_app.js', ``]]) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: The default export is not a React Component in page: \\"/_app\\""` + ) + + await session.patch( + 'pages/_app.js', + outdent` function MyApp({ Component, pageProps }) { return ; } export default MyApp ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await cleanup() - }) - - test('empty _document shows logbox', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([['pages/_document.js', ``]]) - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: The default export is not a React Component in page: \\"/_document\\""` - ) - - await session.patch( - 'pages/_document.js', - outdent` + ) + expect(await session.hasRedbox(false)).toBe(false) + await cleanup() + }) + + test('empty _document shows logbox', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([['pages/_document.js', ``]]) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: The default export is not a React Component in page: \\"/_document\\""` + ) + + await session.patch( + 'pages/_document.js', + outdent` import Document, { Html, Head, Main, NextScript } from 'next/document' class MyDocument extends Document { @@ -71,31 +69,31 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default MyDocument ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await cleanup() - }) - - test('_app syntax error shows logbox', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'pages/_app.js', - outdent` + ) + expect(await session.hasRedbox(false)).toBe(false) + await cleanup() + }) + + test('_app syntax error shows logbox', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'pages/_app.js', + outdent` function MyApp({ Component, pageProps }) { return <; } export default MyApp `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) - expect( - next.normalizeTestDirContent(await session.getRedboxSource()) - ).toMatchInlineSnapshot( - next.normalizeSnapshot(` + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect( + next.normalizeTestDirContent(await session.getRedboxSource()) + ).toMatchInlineSnapshot( + next.normalizeSnapshot(` "./pages/_app.js Error: x Expression expected @@ -119,28 +117,28 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( Caused by: Syntax Error" `) - ) + ) - await session.patch( - 'pages/_app.js', - outdent` + await session.patch( + 'pages/_app.js', + outdent` function MyApp({ Component, pageProps }) { return ; } export default MyApp ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await cleanup() - }) - - test('_document syntax error shows logbox', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'pages/_document.js', - outdent` + ) + expect(await session.hasRedbox(false)).toBe(false) + await cleanup() + }) + + test('_document syntax error shows logbox', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'pages/_document.js', + outdent` import Document, { Html, Head, Main, NextScript } from 'next/document' class MyDocument extends Document {{ @@ -164,14 +162,14 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default MyDocument `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) - expect( - next.normalizeTestDirContent(await session.getRedboxSource()) - ).toMatchInlineSnapshot( - next.normalizeSnapshot(` + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect( + next.normalizeTestDirContent(await session.getRedboxSource()) + ).toMatchInlineSnapshot( + next.normalizeSnapshot(` "./pages/_document.js Error: x Unexpected token \`{\`. Expected identifier, string literal, numeric literal or [ for the computed key @@ -188,11 +186,11 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( Caused by: Syntax Error" `) - ) + ) - await session.patch( - 'pages/_document.js', - outdent` + await session.patch( + 'pages/_document.js', + outdent` import Document, { Html, Head, Main, NextScript } from 'next/document' class MyDocument extends Document { @@ -216,9 +214,8 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default MyDocument ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await cleanup() - }) - } -) + ) + expect(await session.hasRedbox(false)).toBe(false) + await cleanup() + }) +}) diff --git a/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts b/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts index 8abee66f0b9e5..36d51f018f879 100644 --- a/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts +++ b/test/development/acceptance/ReactRefreshLogBox-builtins.test.ts @@ -4,60 +4,58 @@ import { describeVariants as describe } from 'next-test-utils' import { outdent } from 'outdent' import path from 'path' -describe.each(['default', 'turbo', 'experimentalTurbo'])( - 'ReactRefreshLogBox %s', - () => { - const { next } = nextTestSetup({ - files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), - skipStart: true, - }) - - // Module trace is only available with webpack 5 - test('Node.js builtins', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'node_modules/my-package/index.js', - outdent` +describe.each(['default', 'turbo'])('ReactRefreshLogBox %s', () => { + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + skipStart: true, + }) + + // Module trace is only available with webpack 5 + test('Node.js builtins', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'node_modules/my-package/index.js', + outdent` const dns = require('dns') module.exports = dns `, - ], - [ - 'node_modules/my-package/package.json', - outdent` + ], + [ + 'node_modules/my-package/package.json', + outdent` { "name": "my-package", "version": "0.0.1" } `, - ], - ]) - ) + ], + ]) + ) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import pkg from 'my-package' export default function Hello() { return (pkg ?

Package loaded

:

Package did not load

) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Module not found', async () => { - const { session, cleanup } = await sandbox(next) + test('Module not found', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import Comp from 'b' export default function Oops() { @@ -68,22 +66,22 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Module not found (empty import trace)', async () => { - const { session, cleanup } = await sandbox(next) + test('Module not found (empty import trace)', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'pages/index.js', - outdent` + await session.patch( + 'pages/index.js', + outdent` import Comp from 'b' export default function Oops() { @@ -94,59 +92,58 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - test('Module not found (missing global CSS)', async () => { - const { session, cleanup } = await sandbox( - next, - new Map([ - [ - 'pages/_app.js', - outdent` + test('Module not found (missing global CSS)', async () => { + const { session, cleanup } = await sandbox( + next, + new Map([ + [ + 'pages/_app.js', + outdent` import './non-existent.css' export default function App({ Component, pageProps }) { return } `, - ], - [ - 'pages/index.js', - outdent` + ], + [ + 'pages/index.js', + outdent` export default function Page(props) { return

index page

} `, - ], - ]) - ) - expect(await session.hasRedbox(true)).toBe(true) + ], + ]) + ) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatchSnapshot() + const source = await session.getRedboxSource() + expect(source).toMatchSnapshot() - await session.patch( - 'pages/_app.js', - outdent` + await session.patch( + 'pages/_app.js', + outdent` export default function App({ Component, pageProps }) { return } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.documentElement.innerHTML) - ).toContain('index page') - - await cleanup() - }) - } -) + ) + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.documentElement.innerHTML) + ).toContain('index page') + + await cleanup() + }) +}) diff --git a/test/development/acceptance/ReactRefreshLogBox.test.ts b/test/development/acceptance/ReactRefreshLogBox.test.ts index 3928bbb1b9612..2b1290edaed06 100644 --- a/test/development/acceptance/ReactRefreshLogBox.test.ts +++ b/test/development/acceptance/ReactRefreshLogBox.test.ts @@ -5,20 +5,18 @@ import { describeVariants as describe } from 'next-test-utils' import path from 'path' import { outdent } from 'outdent' -describe.each(['default', 'turbo', 'experimentalTurbo'])( - 'ReactRefreshLogBox %s', - () => { - const { next } = nextTestSetup({ - files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), - skipStart: true, - }) - - test('should strip whitespace correctly with newline', async () => { - const { session, cleanup } = await sandbox(next) - - await session.patch( - 'index.js', - outdent` +describe.each(['default', 'turbo'])('ReactRefreshLogBox %s', () => { + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + skipStart: true, + }) + + test('should strip whitespace correctly with newline', async () => { + const { session, cleanup } = await sandbox(next) + + await session.patch( + 'index.js', + outdent` export default function Page() { return ( <> @@ -34,24 +32,24 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - await session.evaluate(() => document.querySelector('a').click()) + ) + await session.evaluate(() => document.querySelector('a').click()) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137807 - test('module init error not shown', async () => { - // Start here: - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137807 + test('module init error not shown', async () => { + // Start here: + const { session, cleanup } = await sandbox(next) - // We start here. - await session.patch( - 'index.js', - outdent` + // We start here. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { render() { @@ -60,16 +58,16 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } export default ClassDefault; ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('h1').textContent) - ).toBe('Default Export') + expect( + await session.evaluate(() => document.querySelector('h1').textContent) + ).toBe('Default Export') - // Add a throw in module init phase: - await session.patch( - 'index.js', - outdent` + // Add a throw in module init phase: + await session.patch( + 'index.js', + outdent` // top offset for snapshot import * as React from 'react'; throw new Error('no') @@ -80,29 +78,29 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } export default ClassDefault; ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554152127 - test('boundaries', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554152127 + test('boundaries', async () => { + const { session, cleanup } = await sandbox(next) - await session.write( - 'FunctionDefault.js', - outdent` + await session.write( + 'FunctionDefault.js', + outdent` export default function FunctionDefault() { return

hello

} ` - ) - await session.patch( - 'index.js', - outdent` + ) + await session.patch( + 'index.js', + outdent` import FunctionDefault from './FunctionDefault.js' import * as React from 'react' class ErrorBoundary extends React.Component { @@ -132,58 +130,58 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } export default App; ` - ) - - expect( - await session.evaluate(() => document.querySelector('h2').textContent) - ).toBe('hello') - - await session.write( - 'FunctionDefault.js', - `export default function FunctionDefault() { throw new Error('no'); }` - ) - - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() - expect( - await session.evaluate(() => document.querySelector('h2').textContent) - ).toBe('error') - - await cleanup() - }) - - // TODO: investigate why this fails when running outside of the Next.js - // monorepo e.g. fails when using yarn create next-app - // https://github.com/vercel/next.js/pull/23203 - test.skip('internal package errors', async () => { - const { session, cleanup } = await sandbox(next) - - // Make a react build-time error. - await session.patch( - 'index.js', - outdent` + ) + + expect( + await session.evaluate(() => document.querySelector('h2').textContent) + ).toBe('hello') + + await session.write( + 'FunctionDefault.js', + `export default function FunctionDefault() { throw new Error('no'); }` + ) + + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() + expect( + await session.evaluate(() => document.querySelector('h2').textContent) + ).toBe('error') + + await cleanup() + }) + + // TODO: investigate why this fails when running outside of the Next.js + // monorepo e.g. fails when using yarn create next-app + // https://github.com/vercel/next.js/pull/23203 + test.skip('internal package errors', async () => { + const { session, cleanup } = await sandbox(next) + + // Make a react build-time error. + await session.patch( + 'index.js', + outdent` export default function FunctionNamed() { return
{{}}
}` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - // We internally only check the script path, not including the line number - // and error message because the error comes from an external library. - // This test ensures that the errored script path is correctly resolved. - expect(await session.getRedboxSource()).toContain( - `../../../../packages/next/dist/pages/_document.js` - ) + expect(await session.hasRedbox(true)).toBe(true) + // We internally only check the script path, not including the line number + // and error message because the error comes from an external library. + // This test ensures that the errored script path is correctly resolved. + expect(await session.getRedboxSource()).toContain( + `../../../../packages/next/dist/pages/_document.js` + ) - await cleanup() - }) + await cleanup() + }) - test('unterminated JSX', async () => { - const { session, cleanup } = await sandbox(next) + test('unterminated JSX', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` export default () => { return (
@@ -192,13 +190,13 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(false)).toBe(false) + expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` export default () => { return (
@@ -207,13 +205,13 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) + expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(next.normalizeTestDirContent(source)).toMatchInlineSnapshot( - next.normalizeSnapshot(` + const source = await session.getRedboxSource() + expect(next.normalizeTestDirContent(source)).toMatchInlineSnapshot( + next.normalizeSnapshot(` "./index.js Error: x Unexpected token. Did you mean \`{'}'}\` or \`}\`? @@ -240,27 +238,27 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ./index.js ./pages/index.js" `) - ) + ) - await cleanup() - }) + await cleanup() + }) - // Module trace is only available with webpack 5 - test('conversion to class component (1)', async () => { - const { session, cleanup } = await sandbox(next) + // Module trace is only available with webpack 5 + test('conversion to class component (1)', async () => { + const { session, cleanup } = await sandbox(next) - await session.write( - 'Child.js', - outdent` + await session.write( + 'Child.js', + outdent` export default function ClickCount() { return

hello

} ` - ) + ) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import Child from './Child'; export default function Home() { @@ -271,16 +269,16 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('hello') + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('hello') - await session.patch( - 'Child.js', - outdent` + await session.patch( + 'Child.js', + outdent` import { Component } from 'react'; export default class ClickCount extends Component { render() { @@ -288,14 +286,14 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchSnapshot() + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchSnapshot() - await session.patch( - 'Child.js', - outdent` + await session.patch( + 'Child.js', + outdent` import { Component } from 'react'; export default class ClickCount extends Component { render() { @@ -303,23 +301,23 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } } ` - ) + ) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('hello new') + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('hello new') - await cleanup() - }) + await cleanup() + }) - test('css syntax errors', async () => { - const { session, cleanup } = await sandbox(next) + test('css syntax errors', async () => { + const { session, cleanup } = await sandbox(next) - await session.write('index.module.css', `.button {}`) - await session.patch( - 'index.js', - outdent` + await session.write('index.module.css', `.button {}`) + await session.patch( + 'index.js', + outdent` import './index.module.css'; export default () => { return ( @@ -329,35 +327,35 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - - expect(await session.hasRedbox(false)).toBe(false) - - // Syntax error - await session.patch('index.module.css', `.button {`) - expect(await session.hasRedbox(true)).toBe(true) - const source = await session.getRedboxSource() - expect(source).toMatch('./index.module.css:1:1') - expect(source).toMatch('Syntax error: ') - expect(source).toMatch('Unclosed block') - expect(source).toMatch('> 1 | .button {') - expect(source).toMatch(' | ^') - - // Not local error - await session.patch('index.module.css', `button {}`) - expect(await session.hasRedbox(true)).toBe(true) - const source2 = await session.getRedboxSource() - expect(source2).toMatchSnapshot() - - await cleanup() - }) - - test('logbox: anchors links in error messages', async () => { - const { session, cleanup } = await sandbox(next) - - await session.patch( - 'index.js', - outdent` + ) + + expect(await session.hasRedbox(false)).toBe(false) + + // Syntax error + await session.patch('index.module.css', `.button {`) + expect(await session.hasRedbox(true)).toBe(true) + const source = await session.getRedboxSource() + expect(source).toMatch('./index.module.css:1:1') + expect(source).toMatch('Syntax error: ') + expect(source).toMatch('Unclosed block') + expect(source).toMatch('> 1 | .button {') + expect(source).toMatch(' | ^') + + // Not local error + await session.patch('index.module.css', `button {}`) + expect(await session.hasRedbox(true)).toBe(true) + const source2 = await session.getRedboxSource() + expect(source2).toMatchSnapshot() + + await cleanup() + }) + + test('logbox: anchors links in error messages', async () => { + const { session, cleanup } = await sandbox(next) + + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -371,39 +369,39 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - - expect(await session.hasRedbox(false)).toBe(false) - await session.evaluate(() => document.querySelector('button').click()) - expect(await session.hasRedbox(true)).toBe(true) - - const header = await session.getRedboxDescription() - expect(header).toMatchSnapshot() - expect( - await session.evaluate( - () => + ) + + expect(await session.hasRedbox(false)).toBe(false) + await session.evaluate(() => document.querySelector('button').click()) + expect(await session.hasRedbox(true)).toBe(true) + + const header = await session.getRedboxDescription() + expect(header).toMatchSnapshot() + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(1) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(1) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() - - await session.patch( - 'index.js', - outdent` + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() + + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -417,39 +415,39 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - - expect(await session.hasRedbox(false)).toBe(false) - await session.evaluate(() => document.querySelector('button').click()) - expect(await session.hasRedbox(true)).toBe(true) - - const header2 = await session.getRedboxDescription() - expect(header2).toMatchSnapshot() - expect( - await session.evaluate( - () => + ) + + expect(await session.hasRedbox(false)).toBe(false) + await session.evaluate(() => document.querySelector('button').click()) + expect(await session.hasRedbox(true)).toBe(true) + + const header2 = await session.getRedboxDescription() + expect(header2).toMatchSnapshot() + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(1) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(1) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() - - await session.patch( - 'index.js', - outdent` + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() + + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -463,39 +461,39 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - - expect(await session.hasRedbox(false)).toBe(false) - await session.evaluate(() => document.querySelector('button').click()) - expect(await session.hasRedbox(true)).toBe(true) - - const header3 = await session.getRedboxDescription() - expect(header3).toMatchSnapshot() - expect( - await session.evaluate( - () => + ) + + expect(await session.hasRedbox(false)).toBe(false) + await session.evaluate(() => document.querySelector('button').click()) + expect(await session.hasRedbox(true)).toBe(true) + + const header3 = await session.getRedboxDescription() + expect(header3).toMatchSnapshot() + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(1) + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(1) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() - - await session.patch( - 'index.js', - outdent` + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() + + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -509,53 +507,53 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - - expect(await session.hasRedbox(false)).toBe(false) - await session.evaluate(() => document.querySelector('button').click()) - expect(await session.hasRedbox(true)).toBe(true) - - const header4 = await session.getRedboxDescription() - expect(header4).toMatchInlineSnapshot( - `"Error: multiple http://nextjs.org links http://example.com"` - ) - expect( - await session.evaluate( - () => + ) + + expect(await session.hasRedbox(false)).toBe(false) + await session.evaluate(() => document.querySelector('button').click()) + expect(await session.hasRedbox(true)).toBe(true) + + const header4 = await session.getRedboxDescription() + expect(header4).toMatchInlineSnapshot( + `"Error: multiple http://nextjs.org links http://example.com"` + ) + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(2) + expect( + await session.evaluate( + () => + ( + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() + expect( + await session.evaluate( + () => + ( document .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(2) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(2)' - ) as any - ).href - ) - ).toMatchSnapshot() - - await session.patch( - 'index.js', - outdent` + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(2)' + ) as any + ).href + ) + ).toMatchSnapshot() + + await session.patch( + 'index.js', + outdent` import { useCallback } from 'react' export default function Index() { @@ -569,59 +567,59 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` + ) + + expect(await session.hasRedbox(false)).toBe(false) + await session.evaluate(() => document.querySelector('button').click()) + expect(await session.hasRedbox(true)).toBe(true) + + const header5 = await session.getRedboxDescription() + expect(header5).toMatchInlineSnapshot( + `"Error: multiple http://nextjs.org links (http://example.com)"` + ) + expect( + await session.evaluate( + () => + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') + .length + ) + ).toBe(2) + expect( + await session.evaluate( + () => + ( + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(1)' + ) as any + ).href + ) + ).toMatchSnapshot() + expect( + await session.evaluate( + () => + ( + document + .querySelector('body > nextjs-portal') + .shadowRoot.querySelector( + '#nextjs__container_errors_desc a:nth-of-type(2)' + ) as any + ).href ) + ).toMatchSnapshot() - expect(await session.hasRedbox(false)).toBe(false) - await session.evaluate(() => document.querySelector('button').click()) - expect(await session.hasRedbox(true)).toBe(true) + await cleanup() + }) - const header5 = await session.getRedboxDescription() - expect(header5).toMatchInlineSnapshot( - `"Error: multiple http://nextjs.org links (http://example.com)"` - ) - expect( - await session.evaluate( - () => - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelectorAll('#nextjs__container_errors_desc a') - .length - ) - ).toBe(2) - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(1)' - ) as any - ).href - ) - ).toMatchSnapshot() - expect( - await session.evaluate( - () => - ( - document - .querySelector('body > nextjs-portal') - .shadowRoot.querySelector( - '#nextjs__container_errors_desc a:nth-of-type(2)' - ) as any - ).href - ) - ).toMatchSnapshot() - - await cleanup() - }) - - test('non-Error errors are handled properly', async () => { - const { session, cleanup } = await sandbox(next) - - await session.patch( - 'index.js', - outdent` + test('non-Error errors are handled properly', async () => { + const { session, cleanup } = await sandbox(next) + + await session.patch( + 'index.js', + outdent` export default () => { throw {'a': 1, 'b': 'x'}; return ( @@ -629,28 +627,28 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: {\\"a\\":1,\\"b\\":\\"x\\"}"` - ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: {\\"a\\":1,\\"b\\":\\"x\\"}"` + ) - // fix previous error - await session.patch( - 'index.js', - outdent` + // fix previous error + await session.patch( + 'index.js', + outdent` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - outdent` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + outdent` class Hello {} export default () => { @@ -660,27 +658,27 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toContain( - `Error: class Hello {` - ) - - // fix previous error - await session.patch( - 'index.js', - outdent` + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toContain( + `Error: class Hello {` + ) + + // fix previous error + await session.patch( + 'index.js', + outdent` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - outdent` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + outdent` export default () => { throw "string error" return ( @@ -688,27 +686,27 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toMatchInlineSnapshot( - `"Error: string error"` - ) - - // fix previous error - await session.patch( - 'index.js', - outdent` + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toMatchInlineSnapshot( + `"Error: string error"` + ) + + // fix previous error + await session.patch( + 'index.js', + outdent` export default () => { return (
hello
) } ` - ) - expect(await session.hasRedbox(false)).toBe(false) - await session.patch( - 'index.js', - outdent` + ) + expect(await session.hasRedbox(false)).toBe(false) + await session.patch( + 'index.js', + outdent` export default () => { throw null return ( @@ -716,13 +714,12 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxDescription()).toContain( - `Error: A null error was thrown` - ) - - await cleanup() - }) - } -) + ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxDescription()).toContain( + `Error: A null error was thrown` + ) + + await cleanup() + }) +}) diff --git a/test/development/acceptance/error-recovery.test.ts b/test/development/acceptance/error-recovery.test.ts index e589c5f1c91fb..b1f7f78b03bc2 100644 --- a/test/development/acceptance/error-recovery.test.ts +++ b/test/development/acceptance/error-recovery.test.ts @@ -5,20 +5,18 @@ import { check, describeVariants as describe } from 'next-test-utils' import { outdent } from 'outdent' import path from 'path' -describe.each(['default', 'turbo', 'experimentalTurbo'])( - 'ReactRefreshLogBox %s', - () => { - const { next } = nextTestSetup({ - files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), - skipStart: true, - }) - - test('logbox: can recover from a syntax error without losing state', async () => { - const { session, cleanup } = await sandbox(next) - - await session.patch( - 'index.js', - outdent` +describe.each(['default', 'turbo'])('ReactRefreshLogBox %s', () => { + const { next } = nextTestSetup({ + files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), + skipStart: true, + }) + + test('logbox: can recover from a syntax error without losing state', async () => { + const { session, cleanup } = await sandbox(next) + + await session.patch( + 'index.js', + outdent` import { useCallback, useState } from 'react' export default function Index() { @@ -32,23 +30,23 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') - await session.patch('index.js', `export default () =>
session.evaluate(() => document.querySelector('p').textContent), - /Count: 1/ - ) + await check( + () => session.evaluate(() => document.querySelector('p').textContent), + /Count: 1/ + ) - expect(await session.hasRedbox(false)).toBe(false) + expect(await session.hasRedbox(false)).toBe(false) - await cleanup() - }) + await cleanup() + }) - test('logbox: can recover from a event handler error', async () => { - const { session, cleanup } = await sandbox(next) + test('logbox: can recover from a event handler error', async () => { + const { session, cleanup } = await sandbox(next) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import { useCallback, useState } from 'react' export default function Index() { @@ -96,18 +94,18 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) - - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('0') - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('1') - - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toMatchInlineSnapshot(` + ) + + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('0') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('1') + + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toMatchInlineSnapshot(` "index.js (7:10) @ eval 5 | const increment = useCallback(() => { @@ -119,9 +117,9 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( 10 |
" `) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import { useCallback, useState } from 'react' export default function Index() { @@ -135,38 +133,38 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect(await session.hasRedbox(false)).toBe(false) + expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Count: 1') - await session.evaluate(() => document.querySelector('button').click()) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Count: 2') + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 1') + await session.evaluate(() => document.querySelector('button').click()) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Count: 2') - expect(await session.hasRedbox(false)).toBe(false) + expect(await session.hasRedbox(false)).toBe(false) - await cleanup() - }) + await cleanup() + }) - test('logbox: can recover from a component error', async () => { - const { session, cleanup } = await sandbox(next) + test('logbox: can recover from a component error', async () => { + const { session, cleanup } = await sandbox(next) - await session.write( - 'child.js', - outdent` + await session.write( + 'child.js', + outdent` export default function Child() { return

Hello

; } ` - ) + ) - await session.patch( - 'index.js', - outdent` + await session.patch( + 'index.js', + outdent` import Child from './child' export default function Index() { @@ -177,53 +175,53 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ) } ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Hello') + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Hello') - await session.patch( - 'child.js', - outdent` + await session.patch( + 'child.js', + outdent` // hello export default function Child() { throw new Error('oops') } ` - ) + ) - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toInclude( - 'export default function Child()' - ) + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toInclude( + 'export default function Child()' + ) - const didNotReload = await session.patch( - 'child.js', - outdent` + const didNotReload = await session.patch( + 'child.js', + outdent` export default function Child() { return

Hello

; } ` - ) + ) - expect(didNotReload).toBe(true) - expect(await session.hasRedbox(false)).toBe(false) - expect( - await session.evaluate(() => document.querySelector('p').textContent) - ).toBe('Hello') + expect(didNotReload).toBe(true) + expect(await session.hasRedbox(false)).toBe(false) + expect( + await session.evaluate(() => document.querySelector('p').textContent) + ).toBe('Hello') - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137262 - test('render error not shown right after syntax error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137262 + test('render error not shown right after syntax error', async () => { + const { session, cleanup } = await sandbox(next) - // Starting here: - await session.patch( - 'index.js', - outdent` + // Starting here: + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { render() { @@ -233,16 +231,16 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default ClassDefault; ` - ) + ) - expect( - await session.evaluate(() => document.querySelector('h1').textContent) - ).toBe('Default Export') + expect( + await session.evaluate(() => document.querySelector('h1').textContent) + ).toBe('Default Export') - // Break it with a syntax error: - await session.patch( - 'index.js', - outdent` + // Break it with a syntax error: + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { @@ -253,13 +251,13 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Now change the code to introduce a runtime error without fixing the syntax error: - await session.patch( - 'index.js', - outdent` + // Now change the code to introduce a runtime error without fixing the syntax error: + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { @@ -271,13 +269,13 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - // Now fix the syntax error: - await session.patch( - 'index.js', - outdent` + // Now fix the syntax error: + await session.patch( + 'index.js', + outdent` import * as React from 'react'; class ClassDefault extends React.Component { @@ -289,29 +287,29 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default ClassDefault; ` - ) - expect(await session.hasRedbox(true)).toBe(true) + ) + expect(await session.hasRedbox(true)).toBe(true) - await check(async () => { - const source = await session.getRedboxSource() - return source?.includes('render() {') ? 'success' : source - }, 'success') + await check(async () => { + const source = await session.getRedboxSource() + return source?.includes('render() {') ? 'success' : source + }, 'success') - expect(await session.getRedboxSource()).toInclude( - "throw new Error('nooo');" - ) + expect(await session.getRedboxSource()).toInclude( + "throw new Error('nooo');" + ) - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554144016 - test('stuck error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554144016 + test('stuck error', async () => { + const { session, cleanup } = await sandbox(next) - // We start here. - await session.patch( - 'index.js', - outdent` + // We start here. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; function FunctionDefault() { @@ -320,23 +318,23 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( export default FunctionDefault; ` - ) + ) - // We add a new file. Let's call it Foo.js. - await session.write( - 'Foo.js', - outdent` + // We add a new file. Let's call it Foo.js. + await session.write( + 'Foo.js', + outdent` // intentionally skips export export default function Foo() { return React.createElement('h1', null, 'Foo'); } ` - ) + ) - // We edit our first file to use it. - await session.patch( - 'index.js', - outdent` + // We edit our first file to use it. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; import Foo from './Foo'; function FunctionDefault() { @@ -344,50 +342,50 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( } export default FunctionDefault; ` - ) - - // We get an error because Foo didn't import React. Fair. - expect(await session.hasRedbox(true)).toBe(true) - expect(await session.getRedboxSource()).toInclude( - "return React.createElement('h1', null, 'Foo');" - ) - - // Let's add that to Foo. - await session.patch( - 'Foo.js', - outdent` + ) + + // We get an error because Foo didn't import React. Fair. + expect(await session.hasRedbox(true)).toBe(true) + expect(await session.getRedboxSource()).toInclude( + "return React.createElement('h1', null, 'Foo');" + ) + + // Let's add that to Foo. + await session.patch( + 'Foo.js', + outdent` import * as React from 'react'; export default function Foo() { return React.createElement('h1', null, 'Foo'); } ` - ) + ) - // Expected: this fixes the problem - expect(await session.hasRedbox(false)).toBe(false) + // Expected: this fixes the problem + expect(await session.hasRedbox(false)).toBe(false) - await cleanup() - }) + await cleanup() + }) - // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554150098 - test('syntax > runtime error', async () => { - const { session, cleanup } = await sandbox(next) + // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554150098 + test('syntax > runtime error', async () => { + const { session, cleanup } = await sandbox(next) - // Start here. - await session.patch( - 'index.js', - outdent` + // Start here. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; export default function FunctionNamed() { return
} ` - ) - // TODO: this acts weird without above step - await session.patch( - 'index.js', - outdent` + ) + // TODO: this acts weird without above step + await session.patch( + 'index.js', + outdent` import * as React from 'react'; let i = 0 setInterval(() => { @@ -398,20 +396,20 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( return
} ` - ) - - await new Promise((resolve) => setTimeout(resolve, 1000)) - expect(await session.hasRedbox(true)).toBe(true) - if (process.platform === 'win32') { - expect(await session.getRedboxSource()).toMatchSnapshot() - } else { - expect(await session.getRedboxSource()).toMatchSnapshot() - } - - // Make a syntax error. - await session.patch( - 'index.js', - outdent` + ) + + await new Promise((resolve) => setTimeout(resolve, 1000)) + expect(await session.hasRedbox(true)).toBe(true) + if (process.platform === 'win32') { + expect(await session.getRedboxSource()).toMatchSnapshot() + } else { + expect(await session.getRedboxSource()).toMatchSnapshot() + } + + // Make a syntax error. + await session.patch( + 'index.js', + outdent` import * as React from 'react'; let i = 0 setInterval(() => { @@ -419,14 +417,14 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( throw Error('no ' + i) }, 1000) export default function FunctionNamed() {` - ) - - await new Promise((resolve) => setTimeout(resolve, 1000)) - expect(await session.hasRedbox(true)).toBe(true) - expect( - next.normalizeTestDirContent(await session.getRedboxSource()) - ).toMatchInlineSnapshot( - next.normalizeSnapshot(` + ) + + await new Promise((resolve) => setTimeout(resolve, 1000)) + expect(await session.hasRedbox(true)).toBe(true) + expect( + next.normalizeTestDirContent(await session.getRedboxSource()) + ).toMatchInlineSnapshot( + next.normalizeSnapshot(` "./index.js Error: x Expected '}', got '' @@ -445,15 +443,15 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ./index.js ./pages/index.js" `) - ) - - // Test that runtime error does not take over: - await new Promise((resolve) => setTimeout(resolve, 2000)) - expect(await session.hasRedbox(true)).toBe(true) - expect( - next.normalizeTestDirContent(await session.getRedboxSource()) - ).toMatchInlineSnapshot( - next.normalizeSnapshot(` + ) + + // Test that runtime error does not take over: + await new Promise((resolve) => setTimeout(resolve, 2000)) + expect(await session.hasRedbox(true)).toBe(true) + expect( + next.normalizeTestDirContent(await session.getRedboxSource()) + ).toMatchInlineSnapshot( + next.normalizeSnapshot(` "./index.js Error: x Expected '}', got '' @@ -472,9 +470,8 @@ describe.each(['default', 'turbo', 'experimentalTurbo'])( ./index.js ./pages/index.js" `) - ) + ) - await cleanup() - }) - } -) + await cleanup() + }) +}) diff --git a/test/lib/e2e-utils.ts b/test/lib/e2e-utils.ts index 9efd2432839ca..1d739f6de28c4 100644 --- a/test/lib/e2e-utils.ts +++ b/test/lib/e2e-utils.ts @@ -7,7 +7,6 @@ import { NextDevInstance } from './next-modes/next-dev' import { NextStartInstance } from './next-modes/next-start' import { NextDeployInstance } from './next-modes/next-deploy' import { shouldRunTurboDevTest } from './next-test-utils' -import { shouldRunExperimentalTurboDevTest } from './turbo' export type { NextInstance } @@ -15,10 +14,7 @@ export type { NextInstance } // if either test runs for the --turbo or have a custom timeout, set reduced timeout instead. // this is due to current --turbo test have a lot of tests fails with timeouts, ends up the whole // test job exceeds the 6 hours limit. -let testTimeout = - shouldRunTurboDevTest() || shouldRunExperimentalTurboDevTest() - ? (240 * 1000) / 4 - : 240 * 1000 +let testTimeout = shouldRunTurboDevTest() ? (240 * 1000) / 4 : 240 * 1000 if (process.env.NEXT_E2E_TEST_TIMEOUT) { try { testTimeout = parseInt(process.env.NEXT_E2E_TEST_TIMEOUT, 10) @@ -151,8 +147,7 @@ export async function createNext( return await trace('createNext').traceAsyncFn(async (rootSpan) => { const useTurbo = !!process.env.TEST_WASM ? false - : opts?.turbo ?? - (shouldRunTurboDevTest() || shouldRunExperimentalTurboDevTest()) + : opts?.turbo ?? shouldRunTurboDevTest() if (testMode === 'dev') { // next dev diff --git a/test/lib/next-test-utils.ts b/test/lib/next-test-utils.ts index 23e1c7761cad2..09c9736d8c552 100644 --- a/test/lib/next-test-utils.ts +++ b/test/lib/next-test-utils.ts @@ -25,11 +25,7 @@ import type { RequestInit, Response } from 'node-fetch' import type { NextServer } from 'next/dist/server/next' import type { BrowserInterface } from './browsers/base' -import { - getTurbopackFlag, - shouldRunExperimentalTurboDevTest, - shouldRunTurboDevTest, -} from './turbo' +import { getTurbopackFlag, shouldRunTurboDevTest } from './turbo' import stripAnsi from 'strip-ansi' export { shouldRunTurboDevTest } @@ -319,7 +315,6 @@ export interface NextDevOptions { bootupMarker?: RegExp nextStart?: boolean turbo?: boolean - experimentalTurbo?: boolean stderr?: false stdout?: false @@ -363,20 +358,13 @@ export function runNextCommandDev( let didResolve = false const bootType = - opts.nextStart || stdOut - ? 'start' - : opts?.experimentalTurbo - ? 'experimentalTurbo' - : opts?.turbo - ? 'turbo' - : 'dev' + opts.nextStart || stdOut ? 'start' : opts?.turbo ? 'turbo' : 'dev' function handleStdout(data) { const message = data.toString() const bootupMarkers = { dev: /✓ ready/i, - turbo: /started server/i, - experimentalTurbo: /✓ ready/i, + turbo: /✓ ready/i, start: /▲ Next.js/i, } @@ -438,11 +426,10 @@ export function launchApp( ) { const options = opts ?? {} const useTurbo = shouldRunTurboDevTest() - const useExperimentalTurbo = shouldRunExperimentalTurboDevTest() return runNextCommandDev( [ - useTurbo || useExperimentalTurbo ? getTurbopackFlag() : undefined, + useTurbo ? getTurbopackFlag() : undefined, dir, '-p', port as string, @@ -451,7 +438,6 @@ export function launchApp( { ...options, turbo: useTurbo, - experimentalTurbo: useExperimentalTurbo, } ) } @@ -1022,30 +1008,23 @@ export function findAllTelemetryEvents(output: string, eventName: string) { return events.filter((e) => e.eventName === eventName).map((e) => e.payload) } -type TestVariants = 'default' | 'turbo' | 'experimentalTurbo' +type TestVariants = 'default' | 'turbo' // WEB-168: There are some differences / incompletes in turbopack implementation enforces jest requires to update // test snapshot when run against turbo. This fn returns describe, or describe.skip dependes on the running context // to avoid force-snapshot update per each runs until turbopack update includes all the changes. export function getSnapshotTestDescribe(variant: TestVariants) { const runningEnv = variant ?? 'default' - if ( - runningEnv !== 'default' && - runningEnv !== 'turbo' && - runningEnv !== 'experimentalTurbo' - ) { + if (runningEnv !== 'default' && runningEnv !== 'turbo') { throw new Error( - `An invalid test env was passed: ${variant} (only "default", "turbo" and "experimentalTurbo" are valid options)` + `An invalid test env was passed: ${variant} (only "default" and "turbo" are valid options)` ) } const shouldRunTurboDev = shouldRunTurboDevTest() - const shouldRunExperimentalTurboDev = shouldRunExperimentalTurboDevTest() const shouldSkip = (runningEnv === 'turbo' && !shouldRunTurboDev) || - (runningEnv === 'experimentalTurbo' && !shouldRunExperimentalTurboDev) || - (runningEnv === 'default' && - (shouldRunTurboDev || shouldRunExperimentalTurboDev)) + (runningEnv === 'default' && shouldRunTurboDev) return shouldSkip ? describe.skip : describe } diff --git a/test/lib/turbo.ts b/test/lib/turbo.ts index e8261cde5dbd2..72754c0e11e5b 100644 --- a/test/lib/turbo.ts +++ b/test/lib/turbo.ts @@ -1,5 +1,4 @@ let loggedTurbopack = false -let loggedExperimentalTurbopack = false /** * Utility function to determine if a given test case needs to run with --turbo. @@ -26,36 +25,9 @@ export function shouldRunTurboDevTest(): boolean { return shouldRunTurboDev } -/** - * Utility function to determine if a given test case needs to run with --experimental-turbo. - * - * This is primarily for the gradual test enablement with latest turbopack upstream changes. - * - * Note: it could be possible to dynamically create test cases itself (createDevTest(): it.each([...])), but - * it makes hard to conform with existing lint rules. Instead, starting off from manual fixture setup and - * update test cases accordingly as turbopack changes enable more test cases. - */ -export function shouldRunExperimentalTurboDevTest(): boolean { - if (!!process.env.TEST_WASM) { - return false - } - - const shouldRunExperimentalTurboDev = !!process.env.EXPERIMENTAL_TURBOPACK - if (shouldRunExperimentalTurboDev && !loggedExperimentalTurbopack) { - require('console').log( - `Running tests with experimental turbopack because environment variable EXPERIMENTAL_TURBOPACK is set` - ) - loggedExperimentalTurbopack = true - } - - return shouldRunExperimentalTurboDev -} - export function getTurbopackFlag(): string { if (!!process.env.TURBOPACK) { return '--turbo' - } else if (!!process.env.EXPERIMENTAL_TURBOPACK) { - return '--experimental-turbo' } else { throw Error(`Cannot get the flag for running turbopack`) } diff --git a/test/turbopack-tests-manifest.js b/test/turbopack-tests-manifest.js index b163f2858e1ea..61e99157470a2 100644 --- a/test/turbopack-tests-manifest.js +++ b/test/turbopack-tests-manifest.js @@ -26,3 +26,215 @@ const enabledTests = [ ] module.exports = { enabledTests } + +/* Old turbopack enabled tests: + + 'test/development/acceptance-app/ReactRefresh.test.ts', + 'test/development/acceptance-app/ReactRefreshLogBoxMisc.test.ts', + 'test/development/acceptance-app/ReactRefreshRequire.test.ts', + // 'test/development/acceptance-app/app-hmr-changes.test.ts', (FLAKY) + 'test/development/acceptance-app/dynamic-error.test.ts', + 'test/development/acceptance-app/version-staleness.test.ts', + 'test/development/acceptance/ReactRefreshLogBox-scss.test.ts', + 'test/development/acceptance/ReactRefreshLogBoxMisc.test.ts', + 'test/development/api-cors-with-rewrite/index.test.ts', + 'test/development/app-dir/multiple-compiles-single-route/multiple-compiles-single-route.test.ts', + // x-ref: below test is flakey and needs to be investigated further + // 'test/development/app-hmr/hmr.test.ts', + 'test/development/basic/define-class-fields.test.ts', + 'test/development/basic/emotion-swc.test.ts', + 'test/development/basic/legacy-decorators.test.ts', + 'test/development/basic/project-directory-rename.test.ts', + 'test/development/basic/tailwind-jit.test.ts', + 'test/development/basic/theme-ui.test.ts', + 'test/development/dotenv-default-expansion/index.test.ts', + 'test/development/jsconfig-path-reloading/index.test.ts', + 'test/development/middleware-warnings/index.test.ts', + 'test/development/project-directory-with-styled-jsx-suffix/index.test.ts', + 'test/development/repeated-dev-edits/repeated-dev-edits.test.ts', + 'test/development/tsconfig-path-reloading/index.test.ts', + 'test/e2e/app-dir/_allow-underscored-root-directory/_allow-underscored-root-directory.test.ts', + 'test/e2e/app-dir/actions/app-action-invalid.test.ts', + 'test/e2e/app-dir/actions/app-action-size-limit-invalid.test.ts', + 'test/e2e/app-dir/app-alias/app-alias.test.ts', + 'test/e2e/app-dir/app-client-cache/client-cache.test.ts', + 'test/e2e/app-dir/app-css-pageextensions/index.test.ts', + 'test/e2e/app-dir/app-edge-root-layout/index.test.ts', + 'test/e2e/app-dir/app-edge/app-edge.test.ts', + 'test/e2e/app-dir/app-prefetch-false/app-prefetch-false.test.ts', + 'test/e2e/app-dir/app-prefetch/prefetching.test.ts', + 'test/e2e/app-dir/app-validation/validation.test.ts', + 'test/e2e/app-dir/asset-prefix/asset-prefix.test.ts', + 'test/e2e/app-dir/async-component-preload/async-component-preload.test.ts', + 'test/e2e/app-dir/autoscroll-with-css-modules/index.test.ts', + 'test/e2e/app-dir/back-button-download-bug/back-button-download-bug.test.ts', + 'test/e2e/app-dir/build-size/index.test.ts', + 'test/e2e/app-dir/global-error/global-error.test.ts', + 'test/e2e/app-dir/hello-world/hello-world.test.ts', + 'test/e2e/app-dir/import/import.test.ts', + 'test/e2e/app-dir/layout-params/layout-params.test.ts', + 'test/e2e/app-dir/metadata-suspense/index.test.ts', + 'test/e2e/app-dir/rewrites-redirects/rewrites-redirects.test.ts', + 'test/e2e/app-dir/route-page-manifest-bug/route-page-manifest-bug.test.ts', + 'test/e2e/app-dir/router-autoscroll/router-autoscroll.test.ts', + 'test/e2e/app-dir/router-stuck-dynamic-static-segment/router-stuck-dynamic-static-segment.test.ts', + 'test/e2e/app-dir/rsc-basic/rsc-basic.test.ts', + 'test/e2e/app-dir/search-params-react-key/layout-params.test.ts', + 'test/e2e/app-dir/searchparams-static-bailout/searchparams-static-bailout.test.ts', + 'test/e2e/app-dir/test-template/{{ toFileName name }}/{{ toFileName name }}.test.ts', + 'test/e2e/app-dir/use-selected-layout-segment-s/use-selected-layout-segment-s.test.ts', + 'test/e2e/app-dir/crypto-globally-available/crypto-globally-available.test.ts', + 'test/e2e/browserslist-extends/index.test.ts', + 'test/e2e/config-promise-export/async-function.test.ts', + 'test/e2e/config-promise-export/promise.test.ts', + 'test/e2e/conflicting-app-page-error/index.test.ts', + 'test/e2e/disable-js-preload/test/index.test.js', + 'test/e2e/hello-world/hello-world.test.ts', + 'test/e2e/i18n-api-support/index.test.ts', + 'test/e2e/i18n-disallow-multiple-locales/i18n-disallow-multiple-locales.test.ts', + 'test/e2e/i18n-ignore-rewrite-source-locale/rewrites-with-basepath.test.ts', + 'test/e2e/i18n-ignore-rewrite-source-locale/rewrites.test.ts', + 'test/e2e/link-with-api-rewrite/index.test.ts', + 'test/e2e/middleware-fetches-with-body/index.test.ts', + 'test/e2e/middleware-shallow-link/index.test.ts', + 'test/e2e/new-link-behavior/child-a-tag-error.test.ts', + 'test/e2e/new-link-behavior/index.test.ts', + 'test/e2e/new-link-behavior/material-ui.test.ts', + 'test/e2e/new-link-behavior/stitches.test.ts', + 'test/e2e/new-link-behavior/typescript.test.ts', + 'test/e2e/next-head/index.test.ts', + 'test/e2e/next-image-forward-ref/index.test.ts', + 'test/e2e/no-eslint-warn-with-no-eslint-config/index.test.ts', + 'test/e2e/nonce-head-manager/index.test.ts', + 'test/e2e/postcss-config-cjs/index.test.ts', + 'test/e2e/proxy-request-with-middleware/test/index.test.ts', + 'test/e2e/repeated-forward-slashes-error/repeated-forward-slashes-error.test.ts', + 'test/e2e/styled-jsx/index.test.ts', + 'test/e2e/test-template/{{ toFileName name }}/{{ toFileName name }}.test.ts', + 'test/e2e/test-utils-tests/basic/basic.test.ts', + 'test/e2e/trailingslash-with-rewrite/index.test.ts', + 'test/e2e/transpile-packages/index.test.ts', + 'test/e2e/type-module-interop/index.test.ts', + 'test/e2e/typescript-version-no-warning/typescript-version-no-warning.test.ts', + 'test/e2e/typescript-version-warning/typescript-version-warning.test.ts', + 'test/e2e/undici-fetch/index.test.ts', + 'test/e2e/yarn-pnp/test/with-eslint.test.ts', + 'test/e2e/yarn-pnp/test/with-next-sass.test.ts', + 'test/integration/404-page-custom-error/test/index.test.js', + 'test/integration/amp-export-validation/test/index.test.js', + 'test/integration/amphtml-custom-validator/test/index.test.js', + 'test/integration/amphtml-fragment-style/test/index.test.js', + 'test/integration/api-body-parser/test/index.test.js', + 'test/integration/api-catch-all/test/index.test.js', + 'test/integration/app-aspath/test/index.test.js', + 'test/integration/app-config-asset-prefix/test/index.test.js', + 'test/integration/app-functional/test/index.test.js', + 'test/integration/auto-export-error-bail/test/index.test.js', + 'test/integration/auto-export-query-error/test/index.test.js', + 'test/integration/bigint/test/index.test.js', + 'test/integration/catches-missing-getStaticProps/test/index.test.js', + 'test/integration/clean-distdir/test/index.test.js', + 'test/integration/client-navigation-a11y/test/index.test.js', + // TODO: re-enable once the logging is aligned + // 'test/integration/config-experimental-warning/test/index.test.js', + 'test/integration/config-schema-check/test/index.test.js', + 'test/integration/config-syntax-error/test/index.test.js', + 'test/integration/config-validation/test/index.test.ts', + 'test/integration/conflicting-ssg-paths/test/index.test.js', + 'test/integration/create-next-app/index.test.ts', + 'test/integration/create-next-app/package-manager.test.ts', + 'test/integration/create-next-app/templates-app.test.ts', + 'test/integration/create-next-app/templates-pages.test.ts', + 'test/integration/css/test/dev-css-handling.test.js', + 'test/integration/custom-error-page-exception/test/index.test.js', + 'test/integration/custom-server-types/test/index.test.js', + 'test/integration/custom-server/test/index.test.js', + 'test/integration/dedupes-scripts/test/index.test.js', + 'test/integration/development-hmr-refresh/test/index.test.js', + 'test/integration/disable-js/test/index.test.js', + 'test/integration/document-head-warnings/test/index.test.js', + 'test/integration/duplicate-pages/test/index.test.js', + 'test/integration/dynamic-require/test/index.test.js', + 'test/integration/dynamic-route-rename/test/index.test.js', + 'test/integration/empty-object-getInitialProps/test/index.test.js', + 'test/integration/errors-on-output-to-public/test/index.test.js', + 'test/integration/errors-on-output-to-static/test/index.test.js', + 'test/integration/eslint/test/lint-cache.test.js', + 'test/integration/eslint/test/next-lint.test.js', + 'test/integration/export-404/test/index.test.js', + 'test/integration/export-default-map/test/index.test.js', + 'test/integration/export-dynamic-pages/test/index.test.js', + 'test/integration/export-fallback-true-error/test/index.test.js', + 'test/integration/export-getInitialProps-warn/test/index.test.js', + 'test/integration/export-image-default/test/index.test.js', + 'test/integration/export-image-loader-legacy/test/index.test.js', + 'test/integration/export-index-not-found-gsp/test/index.test.ts', + 'test/integration/export-intent/test/index.test.js', + 'test/integration/export-no-build/test/index.test.js', + 'test/integration/export-progress-status-message/test/index.test.js', + 'test/integration/export-subfolders/test/index.test.js', + 'test/integration/fallback-modules/test/index.test.js', + 'test/integration/filesystempublicroutes/test/index.test.js', + 'test/integration/firebase-grpc/test/index.test.js', + 'test/integration/future/test/index.test.js', + 'test/integration/gsp-extension/test/index.test.js', + 'test/integration/handles-export-errors/test/index.test.js', + 'test/integration/hashbang/test/index.test.js', + 'test/integration/index-index/test/index.test.js', + 'test/integration/initial-ref/test/index.test.js', + 'test/integration/invalid-config-values/test/index.test.js', + 'test/integration/invalid-page-automatic-static-optimization/test/index.test.js', + 'test/integration/invalid-revalidate-values/test/index.test.js', + 'test/integration/invalid-server-options/test/index.test.js', + 'test/integration/json-serialize-original-error/test/index.test.js', + 'test/integration/legacy-ssg-methods-error/test/index.test.js', + 'test/integration/link-ref/test/index.test.js', + 'test/integration/link-with-multiple-child/test/index.test.js', + 'test/integration/link-without-router/test/index.test.js', + 'test/integration/middleware-build-errors/test/index.test.js', + 'test/integration/middleware-overrides-node.js-api/test/index.test.ts', + 'test/integration/missing-document-component-error/test/index.test.js', + 'test/integration/next-image-legacy/custom-resolver/test/index.test.ts', + 'test/integration/next-image-legacy/no-intersection-observer-fallback/test/index.test.ts', + 'test/integration/next-image-legacy/noscript/test/index.test.ts', + 'test/integration/next-image-legacy/react-virtualized/test/index.test.ts', + 'test/integration/next-image-legacy/unoptimized/test/index.test.ts', + 'test/integration/next-image-new/react-virtualized/test/index.test.ts', + // 'test/integration/next-image-new/unoptimized/test/index.test.ts', (FLAKY) + 'test/integration/no-op-export/test/index.test.js', + 'test/integration/non-next-dist-exclude/test/index.test.js', + 'test/integration/non-standard-node-env-warning/test/index.test.js', + 'test/integration/ondemand/test/index.test.js', + 'test/integration/optional-chaining-nullish-coalescing/test/index.test.js', + 'test/integration/plugin-mdx-rs/test/index.test.js', + 'test/integration/prerender-invalid-catchall-params/test/index.test.js', + 'test/integration/prerender-invalid-paths/test/index.test.js', + 'test/integration/prerender-legacy/test/index.test.js', + 'test/integration/production-build-dir/test/index.test.js', + 'test/integration/production-start-no-build/test/index.test.js', + 'test/integration/router-hash-navigation/test/index.test.js', + 'test/integration/router-prefetch/test/index.test.js', + 'test/integration/router-rerender/test/index.test.js', + 'test/integration/scss/test/dev-css-handling.test.js', + 'test/integration/src-dir-support-double-dir/test/index.test.js', + 'test/integration/ssg-dynamic-routes-404-page/test/index.test.js', + 'test/integration/static-404/test/index.test.js', + 'test/integration/static-page-name/test/index.test.js', + 'test/integration/trailing-slashes-href-resolving/test/index.test.js', + 'test/integration/tsconfig-verifier/test/index.test.js', + 'test/integration/typescript-baseurl/test/index.test.js', + 'test/integration/typescript-external-dir/project/test/index.test.js', + 'test/integration/typescript-filtered-files/test/index.test.js', + // 'test/integration/typescript-hmr/test/index.test.js', (FLAKY) + 'test/integration/webpack-config-mainjs/test/index.test.js', + 'test/integration/with-electron/test/index.test.js', + 'test/production/ci-missing-typescript-deps/index.test.ts', + 'test/production/enoent-during-require/index.test.ts', + 'test/production/eslint-plugin-deps/index.test.ts', + 'test/production/fallback-export-error/index.test.ts', + 'test/production/jest/new-link-behavior.test.ts', + 'test/production/jest/transpile-packages.test.ts', + 'test/production/postcss-plugin-config-as-string/index.test.ts', + 'test/production/supports-module-resolution-nodenext/supports-moduleresolution-nodenext.test.ts', + +*/