diff --git a/docs/01-app/03-api-reference/05-config/01-next-config-js/devIndicators.mdx b/docs/01-app/03-api-reference/05-config/01-next-config-js/devIndicators.mdx index 88e873b99f97..4de054f14172 100644 --- a/docs/01-app/03-api-reference/05-config/01-next-config-js/devIndicators.mdx +++ b/docs/01-app/03-api-reference/05-config/01-next-config-js/devIndicators.mdx @@ -5,16 +5,29 @@ description: Configuration options for the on-screen indicator that gives contex `devIndicators` allows you to configure the on-screen indicator that gives context about the current route you're viewing during development. -```ts filename="Types" - devIndicators: false | { - position?: 'bottom-right' - | 'bottom-left' - | 'top-right' - | 'top-left', // defaults to 'bottom-left', +Open `next.config.ts` and set `position` to choose where the indicator renders. The default is `bottom-left`. + +```ts filename="next.config.ts" +import type { NextConfig } from 'next' + +const nextConfig: NextConfig = { + devIndicators: { + position: 'bottom-right', // 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right' }, +} + +export default nextConfig ``` -Setting `devIndicators` to `false` will hide the indicator, however Next.js will continue to surface any build or runtime errors that were encountered. +To hide the indicator entirely, set `devIndicators` to `false`. Next.js will still surface any compile or runtime errors that were encountered. + +```ts filename="next.config.ts" +const nextConfig: NextConfig = { + devIndicators: false, +} + +export default nextConfig +``` ## Troubleshooting diff --git a/examples/with-cloudinary/.env.local.example b/examples/with-cloudinary/.env.local.example deleted file mode 100644 index c4b7620d9f8f..000000000000 --- a/examples/with-cloudinary/.env.local.example +++ /dev/null @@ -1,4 +0,0 @@ -NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME= -CLOUDINARY_API_KEY= -CLOUDINARY_API_SECRET= -CLOUDINARY_FOLDER= diff --git a/examples/with-cloudinary/README.md b/examples/with-cloudinary/README.md deleted file mode 100644 index ce0aa450eb45..000000000000 --- a/examples/with-cloudinary/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Next.js & Cloudinary example app - -This example shows how to create an image gallery site using Next.js, [Cloudinary](https://cloudinary.com), and [Tailwind](https://tailwindcss.com). - -## Deploy your own - -Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example) or view the demo [here](https://nextconf-images.vercel.app/) - -[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-cloudinary&project-name=nextjs-image-gallery&repository-name=with-cloudinary&env=NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,CLOUDINARY_API_KEY,CLOUDINARY_API_SECRET,CLOUDINARY_FOLDER&envDescription=API%20Keys%20from%20Cloudinary%20needed%20to%20run%20this%20application.) - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. - -## How to use - -Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example: - -```bash -npx create-next-app --example with-cloudinary with-cloudinary-app -``` - -```bash -yarn create next-app --example with-cloudinary with-cloudinary-app -``` - -```bash -pnpm create next-app --example with-cloudinary with-cloudinary-app -``` - -## References - -- Cloudinary API: https://cloudinary.com/documentation/transformation_reference diff --git a/examples/with-cloudinary/pages/p/[photoId].tsx b/examples/with-cloudinary/pages/p/[photoId].tsx deleted file mode 100644 index 043e6846de81..000000000000 --- a/examples/with-cloudinary/pages/p/[photoId].tsx +++ /dev/null @@ -1,77 +0,0 @@ -import type { GetStaticProps, NextPage } from "next"; -import Head from "next/head"; -import { useRouter } from "next/router"; -import Carousel from "../../components/Carousel"; -import getResults from "../../utils/cachedImages"; -import cloudinary from "../../utils/cloudinary"; -import getBase64ImageUrl from "../../utils/generateBlurPlaceholder"; -import type { ImageProps } from "../../utils/types"; - -const Home: NextPage = ({ currentPhoto }: { currentPhoto: ImageProps }) => { - const router = useRouter(); - const { photoId } = router.query; - let index = Number(photoId); - - const currentPhotoUrl = `https://res.cloudinary.com/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/c_scale,w_2560/${currentPhoto.public_id}.${currentPhoto.format}`; - - return ( - <> - - Next.js Conf 2022 Photos - - - -
- -
- - ); -}; - -export default Home; - -export const getStaticProps: GetStaticProps = async (context) => { - const results = await getResults(); - - let reducedResults: ImageProps[] = []; - let i = 0; - for (let result of results.resources) { - reducedResults.push({ - id: i, - height: result.height, - width: result.width, - public_id: result.public_id, - format: result.format, - }); - i++; - } - - const currentPhoto = reducedResults.find( - (img) => img.id === Number(context.params.photoId), - ); - currentPhoto.blurDataUrl = await getBase64ImageUrl(currentPhoto); - - return { - props: { - currentPhoto: currentPhoto, - }, - }; -}; - -export async function getStaticPaths() { - const results = await cloudinary.v2.search - .expression(`folder:${process.env.CLOUDINARY_FOLDER}/*`) - .sort_by("public_id", "desc") - .max_results(400) - .execute(); - - let fullPaths = []; - for (let i = 0; i < results.resources.length; i++) { - fullPaths.push({ params: { photoId: i.toString() } }); - } - - return { - paths: fullPaths, - fallback: false, - }; -} diff --git a/examples/with-cloudinary/utils/cachedImages.ts b/examples/with-cloudinary/utils/cachedImages.ts deleted file mode 100644 index c675418da29d..000000000000 --- a/examples/with-cloudinary/utils/cachedImages.ts +++ /dev/null @@ -1,17 +0,0 @@ -import cloudinary from "./cloudinary"; - -let cachedResults; - -export default async function getResults() { - if (!cachedResults) { - const fetchedResults = await cloudinary.v2.search - .expression(`folder:${process.env.CLOUDINARY_FOLDER}/*`) - .sort_by("public_id", "desc") - .max_results(400) - .execute(); - - cachedResults = fetchedResults; - } - - return cachedResults; -} diff --git a/examples/with-cloudinary/utils/cloudinary.ts b/examples/with-cloudinary/utils/cloudinary.ts deleted file mode 100644 index 17f81a3affaa..000000000000 --- a/examples/with-cloudinary/utils/cloudinary.ts +++ /dev/null @@ -1,10 +0,0 @@ -import cloudinary from "cloudinary"; - -cloudinary.v2.config({ - cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME, - api_key: process.env.CLOUDINARY_API_KEY, - api_secret: process.env.CLOUDINARY_API_SECRET, - secure: true, -}); - -export default cloudinary; diff --git a/examples/with-cloudinary/utils/generateBlurPlaceholder.ts b/examples/with-cloudinary/utils/generateBlurPlaceholder.ts deleted file mode 100644 index bb5bfeb86418..000000000000 --- a/examples/with-cloudinary/utils/generateBlurPlaceholder.ts +++ /dev/null @@ -1,25 +0,0 @@ -import imagemin from "imagemin"; -import imageminJpegtran from "imagemin-jpegtran"; -import type { ImageProps } from "./types"; - -const cache = new Map(); - -export default async function getBase64ImageUrl( - image: ImageProps, -): Promise { - let url = cache.get(image); - if (url) { - return url; - } - const response = await fetch( - `https://res.cloudinary.com/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/f_jpg,w_8,q_70/${image.public_id}.${image.format}`, - ); - const buffer = await response.arrayBuffer(); - const minified = await imagemin.buffer(Buffer.from(buffer), { - plugins: [imageminJpegtran()], - }); - - url = `data:image/jpeg;base64,${Buffer.from(minified).toString("base64")}`; - cache.set(image, url); - return url; -} diff --git a/examples/with-vercel-blob/.env.local.example b/examples/with-vercel-blob/.env.local.example new file mode 100644 index 000000000000..a72126d7a099 --- /dev/null +++ b/examples/with-vercel-blob/.env.local.example @@ -0,0 +1 @@ +BLOB_READ_WRITE_TOKEN= diff --git a/examples/with-cloudinary/.gitignore b/examples/with-vercel-blob/.gitignore similarity index 100% rename from examples/with-cloudinary/.gitignore rename to examples/with-vercel-blob/.gitignore diff --git a/examples/with-vercel-blob/README.md b/examples/with-vercel-blob/README.md new file mode 100644 index 000000000000..8c26aab8d53a --- /dev/null +++ b/examples/with-vercel-blob/README.md @@ -0,0 +1,41 @@ +# Next.js & Vercel Blob example app + +This example shows how to create an image gallery site using Next.js, [Vercel Blob](https://vercel.com/storage/blob), and [Tailwind](https://tailwindcss.com). + +Images are discovered at build time via the `@vercel/blob` SDK's `list()` API, then probed with [sharp](https://github.com/lovell/sharp) to derive dimensions and pre-generate base64 blur placeholders for `next/image`. The blob `url` is served as-is — `next/image` (Vercel's image optimizer) takes care of responsive avif/webp variants. + +## Deploy your own + +Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example): + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-vercel-blob&project-name=nextjs-image-gallery&repository-name=with-vercel-blob&env=BLOB_READ_WRITE_TOKEN&envDescription=Read%2Fwrite%20token%20for%20your%20Vercel%20Blob%20store) + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. + +## How to use + +Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example: + +```bash +npx create-next-app --example with-vercel-blob with-vercel-blob-app +``` + +```bash +yarn create next-app --example with-vercel-blob with-vercel-blob-app +``` + +```bash +pnpm create next-app --example with-vercel-blob with-vercel-blob-app +``` + +## Setup + +1. [Create a Vercel Blob store](https://vercel.com/docs/storage/vercel-blob) and upload your images to its root. +2. Copy `.env.local.example` to `.env.local` and set `BLOB_READ_WRITE_TOKEN` to the store's read/write token. (When deploying via the button above, set it as an environment variable on the project.) +3. `npm install && npm run dev`. + +## References + +- Vercel Blob: https://vercel.com/docs/storage/vercel-blob +- `@vercel/blob` SDK: https://vercel.com/docs/storage/vercel-blob/using-blob-sdk +- `next/image`: https://nextjs.org/docs/api-reference/next/image diff --git a/examples/with-cloudinary/components/Carousel.tsx b/examples/with-vercel-blob/components/Carousel.tsx similarity index 100% rename from examples/with-cloudinary/components/Carousel.tsx rename to examples/with-vercel-blob/components/Carousel.tsx diff --git a/examples/with-cloudinary/components/Icons/Bridge.tsx b/examples/with-vercel-blob/components/Icons/Bridge.tsx similarity index 100% rename from examples/with-cloudinary/components/Icons/Bridge.tsx rename to examples/with-vercel-blob/components/Icons/Bridge.tsx diff --git a/examples/with-cloudinary/components/Icons/Logo.tsx b/examples/with-vercel-blob/components/Icons/Logo.tsx similarity index 100% rename from examples/with-cloudinary/components/Icons/Logo.tsx rename to examples/with-vercel-blob/components/Icons/Logo.tsx diff --git a/examples/with-cloudinary/components/Icons/Twitter.tsx b/examples/with-vercel-blob/components/Icons/Twitter.tsx similarity index 100% rename from examples/with-cloudinary/components/Icons/Twitter.tsx rename to examples/with-vercel-blob/components/Icons/Twitter.tsx diff --git a/examples/with-cloudinary/components/Modal.tsx b/examples/with-vercel-blob/components/Modal.tsx similarity index 100% rename from examples/with-cloudinary/components/Modal.tsx rename to examples/with-vercel-blob/components/Modal.tsx diff --git a/examples/with-cloudinary/components/SharedModal.tsx b/examples/with-vercel-blob/components/SharedModal.tsx similarity index 89% rename from examples/with-cloudinary/components/SharedModal.tsx rename to examples/with-vercel-blob/components/SharedModal.tsx index 6346ba19efa0..4426511b5234 100644 --- a/examples/with-cloudinary/components/SharedModal.tsx +++ b/examples/with-vercel-blob/components/SharedModal.tsx @@ -72,11 +72,7 @@ export default function SharedModal({ className="absolute" > {navigation ? ( - downloadPhoto( - `https://res.cloudinary.com/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/${currentImage.public_id}.${currentImage.format}`, - `${index}.jpg`, - ) + downloadPhoto(currentImage.url, `${index}.jpg`) } className="rounded-full bg-black/50 p-2 text-white/75 backdrop-blur-lg transition hover:bg-black/75 hover:text-white" title="Download fullsize version" @@ -172,7 +165,7 @@ export default function SharedModal({ className="mx-auto mt-6 mb-6 flex aspect-[3/2] h-14" > - {filteredImages.map(({ public_id, format, id }) => ( + {filteredImages.map(({ url, id }) => ( ))} diff --git a/examples/with-cloudinary/next.config.js b/examples/with-vercel-blob/next.config.js similarity index 65% rename from examples/with-cloudinary/next.config.js rename to examples/with-vercel-blob/next.config.js index 4e8f4e5a6340..a2940bea0ad2 100644 --- a/examples/with-cloudinary/next.config.js +++ b/examples/with-vercel-blob/next.config.js @@ -3,9 +3,9 @@ module.exports = { remotePatterns: [ { protocol: "https", - hostname: "res.cloudinary.com", + hostname: "*.public.blob.vercel-storage.com", port: "", - pathname: "/my-account/**", + pathname: "/**", search: "", }, ], diff --git a/examples/with-cloudinary/package.json b/examples/with-vercel-blob/package.json similarity index 82% rename from examples/with-cloudinary/package.json rename to examples/with-vercel-blob/package.json index 4ceabfd957f2..c0a41daa225b 100644 --- a/examples/with-cloudinary/package.json +++ b/examples/with-vercel-blob/package.json @@ -9,17 +9,16 @@ "dependencies": { "@headlessui/react": "^1.7.4", "@heroicons/react": "^2.0.13", - "cloudinary": "^1.32.0", + "@vercel/blob": "^1.0.0", "eslint-config-next": "^13.0.1", "framer-motion": "^7.6.4", - "imagemin": "^8.0.1", - "imagemin-jpegtran": "^7.0.0", "next": "latest", "react": "^18.2.0", "react-dom": "^18.2.0", "react-hooks-global-state": "^2.0.0", "react-swipeable": "^7.0.0", - "react-use-keypress": "^1.3.1" + "react-use-keypress": "^1.3.1", + "sharp": "^0.33.5" }, "devDependencies": { "@types/node": "18.11.9", @@ -29,6 +28,6 @@ "prettier": "^2.7.1", "prettier-plugin-tailwindcss": "^0.1.13", "tailwindcss": "^3.2.1", - "typescript": "4.8.4" + "typescript": "^5.1.0" } } diff --git a/examples/with-cloudinary/pages/_app.tsx b/examples/with-vercel-blob/pages/_app.tsx similarity index 100% rename from examples/with-cloudinary/pages/_app.tsx rename to examples/with-vercel-blob/pages/_app.tsx diff --git a/examples/with-cloudinary/pages/_document.tsx b/examples/with-vercel-blob/pages/_document.tsx similarity index 100% rename from examples/with-cloudinary/pages/_document.tsx rename to examples/with-vercel-blob/pages/_document.tsx diff --git a/examples/with-cloudinary/pages/index.tsx b/examples/with-vercel-blob/pages/index.tsx similarity index 78% rename from examples/with-cloudinary/pages/index.tsx rename to examples/with-vercel-blob/pages/index.tsx index 3856f51a5c0a..aeeeb62d3671 100644 --- a/examples/with-cloudinary/pages/index.tsx +++ b/examples/with-vercel-blob/pages/index.tsx @@ -7,8 +7,7 @@ import { useEffect, useRef } from "react"; import Bridge from "../components/Icons/Bridge"; import Logo from "../components/Icons/Logo"; import Modal from "../components/Modal"; -import cloudinary from "../utils/cloudinary"; -import getBase64ImageUrl from "../utils/generateBlurPlaceholder"; +import getResults from "../utils/cachedImages"; import type { ImageProps } from "../utils/types"; import { useLastViewedPhoto } from "../utils/useLastViewedPhoto"; @@ -67,14 +66,14 @@ const Home: NextPage = ({ images }: { images: ImageProps[] }) => {

Clone and Deploy - {images.map(({ id, public_id, format, blurDataUrl }) => ( + {images.map(({ id, url, blurDataUrl }) => ( { style={{ transform: "translate3d(0, 0, 0)" }} placeholder="blur" blurDataURL={blurDataUrl} - src={`https://res.cloudinary.com/${process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME}/image/upload/c_scale,w_720/${public_id}.${format}`} + src={url} width={720} height={480} sizes="(max-width: 640px) 100vw, @@ -138,37 +137,9 @@ const Home: NextPage = ({ images }: { images: ImageProps[] }) => { export default Home; export async function getStaticProps() { - const results = await cloudinary.v2.search - .expression(`folder:${process.env.CLOUDINARY_FOLDER}/*`) - .sort_by("public_id", "desc") - .max_results(400) - .execute(); - let reducedResults: ImageProps[] = []; - - let i = 0; - for (let result of results.resources) { - reducedResults.push({ - id: i, - height: result.height, - width: result.width, - public_id: result.public_id, - format: result.format, - }); - i++; - } - - const blurImagePromises = results.resources.map((image: ImageProps) => { - return getBase64ImageUrl(image); - }); - const imagesWithBlurDataUrls = await Promise.all(blurImagePromises); - - for (let i = 0; i < reducedResults.length; i++) { - reducedResults[i].blurDataUrl = imagesWithBlurDataUrls[i]; - } - return { props: { - images: reducedResults, + images: await getResults(), }, }; } diff --git a/examples/with-vercel-blob/pages/p/[photoId].tsx b/examples/with-vercel-blob/pages/p/[photoId].tsx new file mode 100644 index 000000000000..02ab6d7bdff2 --- /dev/null +++ b/examples/with-vercel-blob/pages/p/[photoId].tsx @@ -0,0 +1,46 @@ +import type { GetStaticProps, NextPage } from "next"; +import Head from "next/head"; +import { useRouter } from "next/router"; +import Carousel from "../../components/Carousel"; +import getResults from "../../utils/cachedImages"; +import type { ImageProps } from "../../utils/types"; + +const Home: NextPage = ({ currentPhoto }: { currentPhoto: ImageProps }) => { + const router = useRouter(); + const { photoId } = router.query; + let index = Number(photoId); + + return ( + <> + + Next.js Conf 2022 Photos + + + +
+ +
+ + ); +}; + +export default Home; + +export const getStaticProps: GetStaticProps = async (context) => { + const images = await getResults(); + const currentPhoto = images.find( + (img) => img.id === Number(context.params.photoId), + ); + + return { + props: { currentPhoto }, + }; +}; + +export async function getStaticPaths() { + const images = await getResults(); + return { + paths: images.map((_, i) => ({ params: { photoId: i.toString() } })), + fallback: false, + }; +} diff --git a/examples/with-cloudinary/postcss.config.js b/examples/with-vercel-blob/postcss.config.js similarity index 100% rename from examples/with-cloudinary/postcss.config.js rename to examples/with-vercel-blob/postcss.config.js diff --git a/examples/with-cloudinary/public/favicon.ico b/examples/with-vercel-blob/public/favicon.ico similarity index 100% rename from examples/with-cloudinary/public/favicon.ico rename to examples/with-vercel-blob/public/favicon.ico diff --git a/examples/with-cloudinary/public/og-image.png b/examples/with-vercel-blob/public/og-image.png similarity index 100% rename from examples/with-cloudinary/public/og-image.png rename to examples/with-vercel-blob/public/og-image.png diff --git a/examples/with-cloudinary/styles/index.css b/examples/with-vercel-blob/styles/index.css similarity index 100% rename from examples/with-cloudinary/styles/index.css rename to examples/with-vercel-blob/styles/index.css diff --git a/examples/with-cloudinary/tailwind.config.js b/examples/with-vercel-blob/tailwind.config.js similarity index 100% rename from examples/with-cloudinary/tailwind.config.js rename to examples/with-vercel-blob/tailwind.config.js diff --git a/examples/with-cloudinary/tsconfig.json b/examples/with-vercel-blob/tsconfig.json similarity index 100% rename from examples/with-cloudinary/tsconfig.json rename to examples/with-vercel-blob/tsconfig.json diff --git a/examples/with-cloudinary/utils/animationVariants.ts b/examples/with-vercel-blob/utils/animationVariants.ts similarity index 100% rename from examples/with-cloudinary/utils/animationVariants.ts rename to examples/with-vercel-blob/utils/animationVariants.ts diff --git a/examples/with-vercel-blob/utils/cachedImages.ts b/examples/with-vercel-blob/utils/cachedImages.ts new file mode 100644 index 000000000000..70b215c98b47 --- /dev/null +++ b/examples/with-vercel-blob/utils/cachedImages.ts @@ -0,0 +1,48 @@ +import { list } from "@vercel/blob"; +import sharp from "sharp"; +import type { ImageProps } from "./types"; + +let cached: ImageProps[] | null = null; + +async function fetchBuffer(url: string): Promise { + const res = await fetch(url); + if (!res.ok) throw new Error(`${res.status} fetching ${url}`); + return Buffer.from(await res.arrayBuffer()); +} + +async function describe(blobUrl: string): Promise<{ + width: number; + height: number; + blurDataUrl: string; +}> { + const buf = await fetchBuffer(blobUrl); + const meta = await sharp(buf).metadata(); + const placeholder = await sharp(buf) + .resize(10) + .jpeg({ quality: 70 }) + .toBuffer(); + return { + width: meta.width ?? 0, + height: meta.height ?? 0, + blurDataUrl: `data:image/jpeg;base64,${placeholder.toString("base64")}`, + }; +} + +export default async function getResults(): Promise { + if (cached) return cached; + + const { blobs } = await list(); + // Newest-first ordering by pathname (mirrors the original Cloudinary + // `sort_by("public_id", "desc")` behaviour for filenames like + // photo-001.jpg, photo-002.jpg, …). + const sorted = blobs.sort((a, b) => (a.pathname < b.pathname ? 1 : -1)); + + cached = await Promise.all( + sorted.map(async (blob, id) => ({ + id, + url: blob.url, + ...(await describe(blob.url)), + })), + ); + return cached; +} diff --git a/examples/with-cloudinary/utils/downloadPhoto.ts b/examples/with-vercel-blob/utils/downloadPhoto.ts similarity index 100% rename from examples/with-cloudinary/utils/downloadPhoto.ts rename to examples/with-vercel-blob/utils/downloadPhoto.ts diff --git a/examples/with-cloudinary/utils/range.ts b/examples/with-vercel-blob/utils/range.ts similarity index 100% rename from examples/with-cloudinary/utils/range.ts rename to examples/with-vercel-blob/utils/range.ts diff --git a/examples/with-cloudinary/utils/types.ts b/examples/with-vercel-blob/utils/types.ts similarity index 75% rename from examples/with-cloudinary/utils/types.ts rename to examples/with-vercel-blob/utils/types.ts index 933664f52ebb..2b466687da3a 100644 --- a/examples/with-cloudinary/utils/types.ts +++ b/examples/with-vercel-blob/utils/types.ts @@ -1,11 +1,10 @@ /* eslint-disable no-unused-vars */ export interface ImageProps { id: number; - height: string; - width: string; - public_id: string; - format: string; - blurDataUrl?: string; + url: string; + width: number; + height: number; + blurDataUrl: string; } export interface SharedModalProps { diff --git a/examples/with-cloudinary/utils/useLastViewedPhoto.ts b/examples/with-vercel-blob/utils/useLastViewedPhoto.ts similarity index 100% rename from examples/with-cloudinary/utils/useLastViewedPhoto.ts rename to examples/with-vercel-blob/utils/useLastViewedPhoto.ts diff --git a/test/development/acceptance/ReactRefreshLogBox.test.ts b/test/development/acceptance/ReactRefreshLogBox.test.ts index e3b30f440373..bd9cdfde6aff 100644 --- a/test/development/acceptance/ReactRefreshLogBox.test.ts +++ b/test/development/acceptance/ReactRefreshLogBox.test.ts @@ -1,6 +1,6 @@ /* eslint-env jest */ import { createSandbox } from 'development-sandbox' -import { FileRef, nextTestSetup } from 'e2e-utils' +import { FileRef, isReact18, nextTestSetup } from 'e2e-utils' import { getRedboxTotalErrorCount, getRedboxCallStack, @@ -10,8 +10,6 @@ import { import path from 'path' import { outdent } from 'outdent' -const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 - describe('ReactRefreshLogBox', () => { const { isTurbopack, next, isRspack } = nextTestSetup({ files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), diff --git a/test/development/acceptance/error-recovery.test.ts b/test/development/acceptance/error-recovery.test.ts index 846ba9d1aa20..88fc4e7dbd77 100644 --- a/test/development/acceptance/error-recovery.test.ts +++ b/test/development/acceptance/error-recovery.test.ts @@ -1,12 +1,10 @@ /* eslint-env jest */ import { createSandbox } from 'development-sandbox' -import { FileRef, nextTestSetup } from 'e2e-utils' +import { FileRef, isReact18, nextTestSetup } from 'e2e-utils' import { check, retry } from 'next-test-utils' import { outdent } from 'outdent' import path from 'path' -const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 - describe('pages/ error recovery', () => { const { next, isTurbopack, isRspack } = nextTestSetup({ files: new FileRef(path.join(__dirname, 'fixtures', 'default-template')), diff --git a/test/development/acceptance/hydration-error.test.ts b/test/development/acceptance/hydration-error.test.ts index a6b50da069be..7d6572baed2f 100644 --- a/test/development/acceptance/hydration-error.test.ts +++ b/test/development/acceptance/hydration-error.test.ts @@ -1,10 +1,9 @@ import { createSandbox } from 'development-sandbox' -import { FileRef, nextTestSetup } from 'e2e-utils' +import { FileRef, isReact18, nextTestSetup } from 'e2e-utils' import path from 'path' import { outdent } from 'outdent' import { getRedboxTotalErrorCount, retry } from 'next-test-utils' -const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 // https://github.com/facebook/react/blob/main/packages/react-dom/src/__tests__/ReactDOMHydrationDiff-test.js used as a reference describe('Error overlay for hydration errors in Pages router', () => { diff --git a/test/development/api-cors-with-rewrite/index.test.ts b/test/development/api-cors-with-rewrite/index.test.ts index 94fc6e706ac2..80c9900cf7a2 100644 --- a/test/development/api-cors-with-rewrite/index.test.ts +++ b/test/development/api-cors-with-rewrite/index.test.ts @@ -1,37 +1,31 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { fetchViaHTTP } from 'next-test-utils' describe('Rewritten API Requests should pass OPTIONS requests to the api function', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/api/some-endpoint.js': ` - export default (req, res) => { - res.end("successfully hit some-endpoint!") - } - `, - }, - nextConfig: { - rewrites: () => - Promise.resolve({ - beforeFiles: [ - // Nextjs by default requires a /api prefix, let's remove that - { - source: '/:path*', - destination: '/api/:path*', - }, - ], - afterFiles: [], - fallback: [], - }), - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + 'pages/api/some-endpoint.js': ` + export default (req, res) => { + res.end("successfully hit some-endpoint!") + } + `, + }, + nextConfig: { + rewrites: () => + Promise.resolve({ + beforeFiles: [ + // Nextjs by default requires a /api prefix, let's remove that + { + source: '/:path*', + destination: '/api/:path*', + }, + ], + afterFiles: [], + fallback: [], + }), + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('should pass OPTIONS requests to the api function', async () => { const res = await fetchViaHTTP(next.url, '/some-endpoint', null, { diff --git a/test/development/app-dir/ssr-in-rsc/ssr-in-rsc.test.ts b/test/development/app-dir/ssr-in-rsc/ssr-in-rsc.test.ts index b9d377167430..bd8fc9295c04 100644 --- a/test/development/app-dir/ssr-in-rsc/ssr-in-rsc.test.ts +++ b/test/development/app-dir/ssr-in-rsc/ssr-in-rsc.test.ts @@ -1,4 +1,4 @@ -import { nextTestSetup } from 'e2e-utils' +import { isReact18, nextTestSetup } from 'e2e-utils' import { waitForRedbox, waitForNoRedbox, @@ -6,8 +6,6 @@ import { getRedboxSource, } from 'next-test-utils' -const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 - const isRspack = process.env.NEXT_RSPACK !== undefined describe('react-dom/server in React Server environment', () => { diff --git a/test/development/basic/allowed-dev-origins.test.ts b/test/development/basic/allowed-dev-origins.test.ts index 4ef2430a5c58..3d868e54cd08 100644 --- a/test/development/basic/allowed-dev-origins.test.ts +++ b/test/development/basic/allowed-dev-origins.test.ts @@ -1,8 +1,7 @@ import http from 'http' import { join } from 'path' import webdriver from 'next-webdriver' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, NextInstance, nextTestSetup } from 'e2e-utils' import { fetchViaHTTP, findPort, retry } from 'next-test-utils' async function createHostServer() { @@ -123,20 +122,18 @@ async function expectBlockedDevResourceMessage( describe.each(['', '/docs'])( 'allowed-dev-origins, basePath: %p', (basePath: string) => { - let next: NextInstance - describe('default blocking', () => { - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'misc/pages')), - public: new FileRef(join(__dirname, 'misc/public')), - }, - nextConfig: { - basePath, - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'misc/pages')), + public: new FileRef(join(__dirname, 'misc/public')), + }, + nextConfig: { + basePath, + }, + }) + beforeAll(async () => { // render 404 page to generate // "/_next/static/chunks/pages/_app.js" // we need this because not found static assets @@ -152,7 +149,6 @@ describe.each(['', '/docs'])( expect(res.status).toBe(200) }) }) - afterAll(() => next.destroy()) it('should block WebSocket from cross-site', async () => { const { server, port } = await createHostServer() @@ -266,18 +262,18 @@ describe.each(['', '/docs'])( }) describe('configured but not allowlisted origins', () => { - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'misc/pages')), - public: new FileRef(join(__dirname, 'misc/public')), - }, - nextConfig: { - basePath, - allowedDevOrigins: ['127.0.0.1'], - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'misc/pages')), + public: new FileRef(join(__dirname, 'misc/public')), + }, + nextConfig: { + basePath, + allowedDevOrigins: ['127.0.0.1'], + }, + }) + beforeAll(async () => { await next.render(withBasePath(basePath, '/404')) await retry(async () => { @@ -288,7 +284,6 @@ describe.each(['', '/docs'])( expect(res.status).toBe(200) }) }) - afterAll(() => next.destroy()) it('should block websocket requests from configured but non-allowlisted hosts', async () => { const { server, port } = await createHostServer() @@ -328,18 +323,18 @@ describe.each(['', '/docs'])( }) describe('configured allowed origins', () => { - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'misc/pages')), - public: new FileRef(join(__dirname, 'misc/public')), - }, - nextConfig: { - basePath, - allowedDevOrigins: ['127.0.0.1', 'example.vercel.sh'], - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'misc/pages')), + public: new FileRef(join(__dirname, 'misc/public')), + }, + nextConfig: { + basePath, + allowedDevOrigins: ['127.0.0.1', 'example.vercel.sh'], + }, + }) + beforeAll(async () => { // render 404 page to generate // "/_next/static/chunks/pages/_app.js" // since we haven't built any paths by this point @@ -355,7 +350,6 @@ describe.each(['', '/docs'])( expect(res.status).toBe(200) }) }) - afterAll(() => next.destroy()) it('should allow dev WebSocket from configured cross-site', async () => { const { server, port } = await createHostServer() diff --git a/test/development/basic/gssp-ssr-change-reloading/test/index.test.ts b/test/development/basic/gssp-ssr-change-reloading/test/index.test.ts index fccfe459c030..639038fc6907 100644 --- a/test/development/basic/gssp-ssr-change-reloading/test/index.test.ts +++ b/test/development/basic/gssp-ssr-change-reloading/test/index.test.ts @@ -2,9 +2,8 @@ import { join } from 'path' import webdriver from 'next-webdriver' -import { createNext, FileRef } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { waitForNoRedbox, check } from 'next-test-utils' -import { NextInstance } from 'e2e-utils' const installCheckVisible = (browser) => { return browser.eval(`(function() { @@ -24,17 +23,12 @@ const installCheckVisible = (browser) => { } describe('GS(S)P Server-Side Change Reloading', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, '../pages')), - lib: new FileRef(join(__dirname, '../lib')), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, '../pages')), + lib: new FileRef(join(__dirname, '../lib')), + }, }) - afterAll(() => next.destroy()) it('should not reload page when client-side is changed too GSP', async () => { const browser = await webdriver(next.url, '/gsp-blog/first') diff --git a/test/development/basic/legacy-decorators.test.ts b/test/development/basic/legacy-decorators.test.ts index 80e5dba2ebad..5fda2339c791 100644 --- a/test/development/basic/legacy-decorators.test.ts +++ b/test/development/basic/legacy-decorators.test.ts @@ -1,31 +1,25 @@ import { join } from 'path' import webdriver from 'next-webdriver' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { check } from 'next-test-utils' describe('Legacy decorators SWC option', () => { describe('with extended tsconfig', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'tsconfig.json': new FileRef( - join(__dirname, 'legacy-decorators/tsconfig-extended.json') - ), - 'tsconfig-base.json': new FileRef( - join(__dirname, 'legacy-decorators/jsconfig.json') - ), - pages: new FileRef(join(__dirname, 'legacy-decorators/pages')), - }, - dependencies: { - mobx: '6.3.7', - 'mobx-react': '7.2.1', - }, - }) + const { next } = nextTestSetup({ + files: { + 'tsconfig.json': new FileRef( + join(__dirname, 'legacy-decorators/tsconfig-extended.json') + ), + 'tsconfig-base.json': new FileRef( + join(__dirname, 'legacy-decorators/jsconfig.json') + ), + pages: new FileRef(join(__dirname, 'legacy-decorators/pages')), + }, + dependencies: { + mobx: '6.3.7', + 'mobx-react': '7.2.1', + }, }) - afterAll(() => next.destroy()) it('should compile with legacy decorators enabled from extended config', async () => { let browser @@ -47,22 +41,18 @@ describe('Legacy decorators SWC option', () => { }) describe('with base config', () => { - let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: { - 'jsconfig.json': new FileRef( - join(__dirname, 'legacy-decorators/jsconfig.json') - ), - pages: new FileRef(join(__dirname, 'legacy-decorators/pages')), - }, - dependencies: { - mobx: '6.3.7', - 'mobx-react': '7.2.1', - }, - }) + const { next } = nextTestSetup({ + files: { + 'jsconfig.json': new FileRef( + join(__dirname, 'legacy-decorators/jsconfig.json') + ), + pages: new FileRef(join(__dirname, 'legacy-decorators/pages')), + }, + dependencies: { + mobx: '6.3.7', + 'mobx-react': '7.2.1', + }, }) - afterAll(() => next.destroy()) it('should compile with legacy decorators enabled', async () => { let browser diff --git a/test/development/basic/misc.test.ts b/test/development/basic/misc.test.ts index 10de4fed5990..7794fb6404d1 100644 --- a/test/development/basic/misc.test.ts +++ b/test/development/basic/misc.test.ts @@ -1,26 +1,20 @@ import { join } from 'path' import webdriver from 'next-webdriver' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils' describe.each([[''], ['/docs']])( 'misc basic dev tests, basePath: %p', (basePath: string) => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'misc/pages')), - public: new FileRef(join(__dirname, 'misc/public')), - }, - nextConfig: { - basePath, - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'misc/pages')), + public: new FileRef(join(__dirname, 'misc/public')), + }, + nextConfig: { + basePath, + }, }) - afterAll(() => next.destroy()) it('should set process.env.NODE_ENV in development', async () => { const browser = await webdriver(next.url, basePath + '/process-env') diff --git a/test/development/basic/next-dynamic/next-dynamic.test.ts b/test/development/basic/next-dynamic/next-dynamic.test.ts index 2bb60a0d72a5..82eb06f3eda5 100644 --- a/test/development/basic/next-dynamic/next-dynamic.test.ts +++ b/test/development/basic/next-dynamic/next-dynamic.test.ts @@ -1,9 +1,8 @@ import { join } from 'path' import cheerio from 'cheerio' import webdriver from 'next-webdriver' -import { createNext, FileRef } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { waitForNoRedbox, renderViaHTTP, check } from 'next-test-utils' -import { NextInstance } from 'e2e-utils' const customDocumentGipContent = `\ import { Html, Main, NextScript, Head } from 'next/document' @@ -29,28 +28,23 @@ const basePath = process.env.TEST_BASE_PATH || '' const srcPrefix = process.env.TEST_SRC_DIR ? 'src/' : '' describe('next/dynamic', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - [`${srcPrefix}/components`]: new FileRef(join(__dirname, 'components')), - [`${srcPrefix}/pages`]: new FileRef(join(__dirname, 'pages')), - ...(process.env.TEST_CUSTOMIZED_DOCUMENT === '1' && { - [`${srcPrefix}/pages/_document.js`]: customDocumentGipContent, + const { next } = nextTestSetup({ + files: { + [`${srcPrefix}/components`]: new FileRef(join(__dirname, 'components')), + [`${srcPrefix}/pages`]: new FileRef(join(__dirname, 'pages')), + ...(process.env.TEST_CUSTOMIZED_DOCUMENT === '1' && { + [`${srcPrefix}/pages/_document.js`]: customDocumentGipContent, + }), + // When it's not turbopack and babel is enabled, we add a .babelrc file. + ...(!process.env.IS_TURBOPACK_TEST && + process.env.TEST_BABEL === '1' && { + '.babelrc': `{ "presets": ["next/babel"] }`, }), - // When it's not turbopack and babel is enabled, we add a .babelrc file. - ...(!process.env.IS_TURBOPACK_TEST && - process.env.TEST_BABEL === '1' && { - '.babelrc': `{ "presets": ["next/babel"] }`, - }), - }, - nextConfig: { - basePath, - }, - }) + }, + nextConfig: { + basePath, + }, }) - afterAll(() => next.destroy()) async function get$(path, query?: any) { const html = await renderViaHTTP(next.url, path, query) diff --git a/test/development/basic/styled-components-disabled.test.ts b/test/development/basic/styled-components-disabled.test.ts index ce3c3b346798..38237aeb1692 100644 --- a/test/development/basic/styled-components-disabled.test.ts +++ b/test/development/basic/styled-components-disabled.test.ts @@ -1,28 +1,22 @@ import { join } from 'path' import webdriver from 'next-webdriver' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { retry } from 'next-test-utils' // TODO: Somehow the warning doesn't show up with Turbopack, even though the transform is not enabled. // TODO: It no longer shows up with Webpack either in tests. describe.skip('styled-components SWC transform', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'next.config.js': new FileRef( - join(__dirname, 'styled-components-disabled/next.config.js') - ), - pages: new FileRef(join(__dirname, 'styled-components-disabled/pages')), - }, - dependencies: { - 'styled-components': '6.1.16', - }, - }) + const { next } = nextTestSetup({ + files: { + 'next.config.js': new FileRef( + join(__dirname, 'styled-components-disabled/next.config.js') + ), + pages: new FileRef(join(__dirname, 'styled-components-disabled/pages')), + }, + dependencies: { + 'styled-components': '6.1.16', + }, }) - afterAll(() => next.destroy()) it('should have hydration mismatch with styled-components transform disabled', async () => { let browser diff --git a/test/development/browser-logs/browser-logs.test.ts b/test/development/browser-logs/browser-logs.test.ts index b0ef324d9a10..650aa759c5c7 100644 --- a/test/development/browser-logs/browser-logs.test.ts +++ b/test/development/browser-logs/browser-logs.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import webdriver from 'next-webdriver' import { join } from 'path' import stripAnsi from 'strip-ansi' @@ -46,28 +45,26 @@ function setupLogCapture() { describe(`Terminal Logging (${bundlerName})`, () => { describe('Pages Router', () => { - let next: NextInstance let logs: string[] = [] let logCapture: ReturnType let browser = null - beforeAll(async () => { + beforeAll(() => { logCapture = setupLogCapture() logs = logCapture.logs + }) - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'fixtures/pages')), - 'next.config.js': new FileRef( - join(__dirname, 'fixtures/next.config.js') - ), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'fixtures/pages')), + 'next.config.js': new FileRef( + join(__dirname, 'fixtures/next.config.js') + ), + }, }) - afterAll(async () => { + afterAll(() => { logCapture.restore() - await next.destroy() }) beforeEach(() => { @@ -152,27 +149,25 @@ describe(`Terminal Logging (${bundlerName})`, () => { }) describe('App Router - Server Components', () => { - let next: NextInstance let logs: string[] = [] let logCapture: ReturnType - beforeAll(async () => { + beforeAll(() => { logCapture = setupLogCapture() logs = logCapture.logs + }) - next = await createNext({ - files: { - app: new FileRef(join(__dirname, 'fixtures/app')), - 'next.config.js': new FileRef( - join(__dirname, 'fixtures/next.config.js') - ), - }, - }) + const { next } = nextTestSetup({ + files: { + app: new FileRef(join(__dirname, 'fixtures/app')), + 'next.config.js': new FileRef( + join(__dirname, 'fixtures/next.config.js') + ), + }, }) - afterAll(async () => { + afterAll(() => { logCapture.restore() - await next.destroy() }) beforeEach(() => { @@ -212,27 +207,25 @@ describe(`Terminal Logging (${bundlerName})`, () => { }) describe('App Router - Client Components', () => { - let next: NextInstance let logs: string[] = [] let logCapture: ReturnType - beforeAll(async () => { + beforeAll(() => { logCapture = setupLogCapture() logs = logCapture.logs + }) - next = await createNext({ - files: { - app: new FileRef(join(__dirname, 'fixtures/app')), - 'next.config.js': new FileRef( - join(__dirname, 'fixtures/next.config.js') - ), - }, - }) + const { next } = nextTestSetup({ + files: { + app: new FileRef(join(__dirname, 'fixtures/app')), + 'next.config.js': new FileRef( + join(__dirname, 'fixtures/next.config.js') + ), + }, }) - afterAll(async () => { + afterAll(() => { logCapture.restore() - await next.destroy() }) beforeEach(() => { @@ -274,27 +267,25 @@ describe(`Terminal Logging (${bundlerName})`, () => { }) describe('App Router - Hydration Errors', () => { - let next: NextInstance let logs: string[] = [] let logCapture: ReturnType - beforeAll(async () => { + beforeAll(() => { logCapture = setupLogCapture() logs = logCapture.logs + }) - next = await createNext({ - files: { - app: new FileRef(join(__dirname, 'fixtures/app')), - 'next.config.js': new FileRef( - join(__dirname, 'fixtures/next.config.js') - ), - }, - }) + const { next } = nextTestSetup({ + files: { + app: new FileRef(join(__dirname, 'fixtures/app')), + 'next.config.js': new FileRef( + join(__dirname, 'fixtures/next.config.js') + ), + }, }) - afterAll(async () => { + afterAll(() => { logCapture.restore() - await next.destroy() }) beforeEach(() => { @@ -374,27 +365,25 @@ describe(`Terminal Logging (${bundlerName})`, () => { }) describe('App Router - Edge Runtime', () => { - let next: NextInstance let logs: string[] = [] let logCapture: ReturnType - beforeAll(async () => { + beforeAll(() => { logCapture = setupLogCapture() logs = logCapture.logs + }) - next = await createNext({ - files: { - app: new FileRef(join(__dirname, 'fixtures/app')), - 'next.config.js': new FileRef( - join(__dirname, 'fixtures/next.config.js') - ), - }, - }) + const { next } = nextTestSetup({ + files: { + app: new FileRef(join(__dirname, 'fixtures/app')), + 'next.config.js': new FileRef( + join(__dirname, 'fixtures/next.config.js') + ), + }, }) - afterAll(async () => { + afterAll(() => { logCapture.restore() - await next.destroy() }) beforeEach(() => { diff --git a/test/development/correct-tsconfig-defaults/index.test.ts b/test/development/correct-tsconfig-defaults/index.test.ts index 71eeff35d35a..fa279dd50bb7 100644 --- a/test/development/correct-tsconfig-defaults/index.test.ts +++ b/test/development/correct-tsconfig-defaults/index.test.ts @@ -1,24 +1,18 @@ -import { createNext } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { check } from 'next-test-utils' -import { NextInstance } from 'e2e-utils' describe('correct tsconfig.json defaults', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.tsx': 'export default function Page() {}', - }, - skipStart: true, - dependencies: { - typescript: 'latest', - '@types/react': 'latest', - '@types/node': 'latest', - }, - }) + const { next } = nextTestSetup({ + files: { + 'pages/index.tsx': 'export default function Page() {}', + }, + skipStart: true, + dependencies: { + typescript: 'latest', + '@types/react': 'latest', + '@types/node': 'latest', + }, }) - afterAll(() => next.destroy()) it('should add `moduleResolution` when generating tsconfig.json in dev', async () => { try { diff --git a/test/development/dotenv-default-expansion/index.test.ts b/test/development/dotenv-default-expansion/index.test.ts index 48f36618aa4d..3f4af6dc9a91 100644 --- a/test/development/dotenv-default-expansion/index.test.ts +++ b/test/development/dotenv-default-expansion/index.test.ts @@ -1,26 +1,20 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import webdriver from 'next-webdriver' describe('Dotenv default expansion', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - export default function Page() { - return

{process.env.NEXT_PUBLIC_TEST}

- } - `, - '.env': ` - NEXT_PUBLIC_TEST=\${MISSING_KEY:-default} - `, - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + export default function Page() { + return

{process.env.NEXT_PUBLIC_TEST}

+ } + `, + '.env': ` + NEXT_PUBLIC_TEST=\${MISSING_KEY:-default} + `, + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('should work', async () => { const browser = await webdriver(next.url, '/') diff --git a/test/development/gssp-notfound/index.test.ts b/test/development/gssp-notfound/index.test.ts index dfa256a3eed7..72c72a8136a6 100644 --- a/test/development/gssp-notfound/index.test.ts +++ b/test/development/gssp-notfound/index.test.ts @@ -1,28 +1,22 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { waitFor } from 'next-test-utils' import webdriver from 'next-webdriver' describe('getServerSideProps returns notFound: true', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - const Home = () => null - export default Home - - export function getServerSideProps() { - console.log("gssp called") - return { notFound: true } - } - `, - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + const Home = () => null + export default Home + + export function getServerSideProps() { + console.log("gssp called") + return { notFound: true } + } + `, + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('should not poll indefinitely', async () => { const browser = await webdriver(next.url, '/') diff --git a/test/development/jsconfig-path-reloading/index.test.ts b/test/development/jsconfig-path-reloading/index.test.ts index 3eebdc74bac9..292986de0877 100644 --- a/test/development/jsconfig-path-reloading/index.test.ts +++ b/test/development/jsconfig-path-reloading/index.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { waitForRedbox, waitForNoRedbox, @@ -13,40 +12,38 @@ import webdriver from 'next-webdriver' import fs from 'fs-extra' describe('jsconfig-path-reloading', () => { - let next: NextInstance const tsConfigFile = 'jsconfig.json' const indexPage = 'pages/index.js' - function runTests({ addAfterStart }: { addAfterStart?: boolean }) { - beforeAll(async () => { - let tsConfigContent = await fs.readFile( - join(__dirname, 'app/jsconfig.json'), - 'utf8' - ) + const tsConfigContent = fs.readFileSync( + join(__dirname, 'app/jsconfig.json'), + 'utf8' + ) - next = await createNext({ - files: { - components: new FileRef(join(__dirname, 'app/components')), - pages: new FileRef(join(__dirname, 'app/pages')), - lib: new FileRef(join(__dirname, 'app/lib')), - ...(addAfterStart - ? {} - : { - [tsConfigFile]: tsConfigContent, - }), - }, - dependencies: { - typescript: 'latest', - '@types/react': 'latest', - '@types/node': 'latest', - }, - }) + function runTests({ addAfterStart }: { addAfterStart?: boolean }) { + const { next } = nextTestSetup({ + files: { + components: new FileRef(join(__dirname, 'app/components')), + pages: new FileRef(join(__dirname, 'app/pages')), + lib: new FileRef(join(__dirname, 'app/lib')), + ...(addAfterStart + ? {} + : { + [tsConfigFile]: tsConfigContent, + }), + }, + dependencies: { + typescript: 'latest', + '@types/react': 'latest', + '@types/node': 'latest', + }, + }) - if (addAfterStart) { + if (addAfterStart) { + beforeAll(async () => { await next.patchFile(tsConfigFile, tsConfigContent) - } - }) - afterAll(() => next.destroy()) + }) + } it('should load with initial paths config correctly', async () => { const html = await renderViaHTTP(next.url, '/') diff --git a/test/development/next-font/build-errors.test.ts b/test/development/next-font/build-errors.test.ts index 8be590ed1994..1d3db5aa2441 100644 --- a/test/development/next-font/build-errors.test.ts +++ b/test/development/next-font/build-errors.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { join } from 'path' import webdriver from 'next-webdriver' import { @@ -10,14 +9,9 @@ import { // TODO: The error overlay is not closed when restoring the working code. describe.skip('next/font build-errors', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef(join(__dirname, 'build-errors')), - }) + const { next } = nextTestSetup({ + files: new FileRef(join(__dirname, 'build-errors')), }) - afterAll(() => next.destroy()) it('should show a next/font error when input is wrong', async () => { const browser = await webdriver(next.url, '/') diff --git a/test/development/next-font/font-loader-in-document-error.test.ts b/test/development/next-font/font-loader-in-document-error.test.ts index 21ef5e992694..021a38da3ca8 100644 --- a/test/development/next-font/font-loader-in-document-error.test.ts +++ b/test/development/next-font/font-loader-in-document-error.test.ts @@ -1,20 +1,14 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { waitForRedbox, getRedboxSource } from 'next-test-utils' import webdriver from 'next-webdriver' import { join } from 'path' describe('font-loader-in-document-error', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'font-loader-in-document/pages')), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'font-loader-in-document/pages')), + }, }) - afterAll(() => next.destroy()) test('next/font inside _document', async () => { const browser = await webdriver(next.url, '/') diff --git a/test/development/pages-dir/client-navigation/index.test.ts b/test/development/pages-dir/client-navigation/index.test.ts index 2c10dcf1ff37..59d6a3afe713 100644 --- a/test/development/pages-dir/client-navigation/index.test.ts +++ b/test/development/pages-dir/client-navigation/index.test.ts @@ -7,7 +7,7 @@ import { getRedboxTotalErrorCount, } from 'next-test-utils' import path from 'path' -import { nextTestSetup } from 'e2e-utils' +import { isReact18, nextTestSetup } from 'e2e-utils' describe('Client Navigation', () => { const { isTurbopack, next, isRspack } = nextTestSetup({ @@ -64,7 +64,7 @@ describe('Client Navigation', () => { it('should always replace the state', async () => { const browser = await next.browser('/nav') - const countAfterClicked = await browser + await browser .elementByCss('#self-reload-link') .click() .waitForElementByCss('#self-reload-page') @@ -72,11 +72,12 @@ describe('Client Navigation', () => { .click() .elementByCss('#self-reload-link') .click() - .elementByCss('p') - .text() - // counts (page change + two clicks) - expect(countAfterClicked).toBe('COUNT: 3') + // counts (page change + two clicks). Use `retry()` because the third + // click's state update may not have flushed when we read the text. + await retry(async () => { + expect(await browser.elementByCss('p').text()).toBe('COUNT: 3') + }) // Since we replace the state, back button would simply go us back to /nav await browser.back().waitForElementByCss('.nav-home') @@ -258,7 +259,6 @@ describe('Client Navigation', () => { describe('runtime errors', () => { it('should show redbox when a client side error is thrown inside a component', async () => { - const isReact18 = process.env.NEXT_TEST_REACT_VERSION?.startsWith('18') const pageErrors: unknown[] = [] const browser = await next.browser('/error-inside-browser-page', { beforePageLoad: (page) => { diff --git a/test/development/pages-dir/client-navigation/rendering-head.test.ts b/test/development/pages-dir/client-navigation/rendering-head.test.ts index 83290ab81bcb..812203d5fd57 100644 --- a/test/development/pages-dir/client-navigation/rendering-head.test.ts +++ b/test/development/pages-dir/client-navigation/rendering-head.test.ts @@ -1,12 +1,10 @@ /* eslint-env jest */ import cheerio from 'cheerio' -import { nextTestSetup } from 'e2e-utils' +import { isReact18, nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' import path from 'path' -const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 - describe('Client Navigation rendering ', () => { const { next } = nextTestSetup({ files: path.join(__dirname, 'fixture'), diff --git a/test/development/pages-dir/client-navigation/rendering.test.ts b/test/development/pages-dir/client-navigation/rendering.test.ts index f789c97e49f6..fa0de35423a9 100644 --- a/test/development/pages-dir/client-navigation/rendering.test.ts +++ b/test/development/pages-dir/client-navigation/rendering.test.ts @@ -1,14 +1,12 @@ /* eslint-env jest */ import cheerio from 'cheerio' -import { nextTestSetup } from 'e2e-utils' +import { isReact18, nextTestSetup } from 'e2e-utils' import { fetchViaHTTP, getDistDir, renderViaHTTP } from 'next-test-utils' import webdriver from 'next-webdriver' import { BUILD_MANIFEST, REACT_LOADABLE_MANIFEST } from 'next/constants' import path from 'path' -const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 - describe('Client Navigation rendering', () => { const { isTurbopack, next, isRspack } = nextTestSetup({ files: path.join(__dirname, 'fixture'), diff --git a/test/development/project-directory-with-styled-jsx-suffix/index.test.ts b/test/development/project-directory-with-styled-jsx-suffix/index.test.ts index 7db06de94543..0f7252488946 100644 --- a/test/development/project-directory-with-styled-jsx-suffix/index.test.ts +++ b/test/development/project-directory-with-styled-jsx-suffix/index.test.ts @@ -1,23 +1,17 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' describe('project directory with styled-jsx suffix', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - export default function Page() { - return

hello world

- } - `, - }, - subDir: 'test-styled-jsx', - }) + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + export default function Page() { + return

hello world

+ } + `, + }, + subDir: 'test-styled-jsx', }) - afterAll(() => next.destroy()) it('should work', async () => { const html = await renderViaHTTP(next.url, '/') diff --git a/test/development/tsconfig-path-reloading/index.test.ts b/test/development/tsconfig-path-reloading/index.test.ts index ba24ad702e23..769e3048dd41 100644 --- a/test/development/tsconfig-path-reloading/index.test.ts +++ b/test/development/tsconfig-path-reloading/index.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { waitForRedbox, waitForNoRedbox, @@ -13,10 +12,14 @@ import webdriver from 'next-webdriver' import fs from 'fs-extra' describe('tsconfig-path-reloading', () => { - let next: NextInstance const tsConfigFile = 'tsconfig.json' const indexPage = 'pages/index.tsx' + const tsConfigContent = fs.readFileSync( + join(__dirname, 'app/tsconfig.json'), + 'utf8' + ) + function runTests({ addAfterStart, testBaseUrl, @@ -24,37 +27,31 @@ describe('tsconfig-path-reloading', () => { addAfterStart?: boolean testBaseUrl: boolean }) { - beforeAll(async () => { - let tsConfigContent = await fs.readFile( - join(__dirname, 'app/tsconfig.json'), - 'utf8' - ) - - const typescriptVersion = testBaseUrl ? '5.9.3' : 'latest' - - next = await createNext({ - files: { - components: new FileRef(join(__dirname, 'app/components')), - pages: new FileRef(join(__dirname, 'app/pages')), - lib: new FileRef(join(__dirname, 'app/lib')), - ...(addAfterStart - ? {} - : { - [tsConfigFile]: tsConfigContent, - }), - }, - dependencies: { - typescript: typescriptVersion, - '@types/react': 'latest', - '@types/node': 'latest', - }, - }) + const typescriptVersion = testBaseUrl ? '5.9.3' : 'latest' + + const { next } = nextTestSetup({ + files: { + components: new FileRef(join(__dirname, 'app/components')), + pages: new FileRef(join(__dirname, 'app/pages')), + lib: new FileRef(join(__dirname, 'app/lib')), + ...(addAfterStart + ? {} + : { + [tsConfigFile]: tsConfigContent, + }), + }, + dependencies: { + typescript: typescriptVersion, + '@types/react': 'latest', + '@types/node': 'latest', + }, + }) - if (addAfterStart) { + if (addAfterStart) { + beforeAll(async () => { await next.patchFile(tsConfigFile, tsConfigContent) - } - }) - afterAll(() => next.destroy()) + }) + } it('should load with initial paths config correctly', async () => { const html = await renderViaHTTP(next.url, '/') diff --git a/test/development/typescript-auto-install/index.test.ts b/test/development/typescript-auto-install/index.test.ts index 3bc0f6318f17..ea0590f98188 100644 --- a/test/development/typescript-auto-install/index.test.ts +++ b/test/development/typescript-auto-install/index.test.ts @@ -1,35 +1,29 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { check, renderViaHTTP } from 'next-test-utils' import webdriver from 'next-webdriver' import stripAnsi from 'strip-ansi' describe('typescript-auto-install', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - export default function Page() { - return

hello world

- } - `, - }, - env: { - // unset CI env as this skips the auto-install behavior - // being tested - CI: '', - CIRCLECI: '', - GITHUB_ACTIONS: '', - CONTINUOUS_INTEGRATION: '', - RUN_ID: '', - BUILD_NUMBER: '', - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + export default function Page() { + return

hello world

+ } + `, + }, + env: { + // unset CI env as this skips the auto-install behavior + // being tested + CI: '', + CIRCLECI: '', + GITHUB_ACTIONS: '', + CONTINUOUS_INTEGRATION: '', + RUN_ID: '', + BUILD_NUMBER: '', + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('should work', async () => { const html = await renderViaHTTP(next.url, '/') diff --git a/test/development/webpack-issuer-deprecation-warning/index.test.ts b/test/development/webpack-issuer-deprecation-warning/index.test.ts index 193717b3688e..0a01bce66603 100644 --- a/test/development/webpack-issuer-deprecation-warning/index.test.ts +++ b/test/development/webpack-issuer-deprecation-warning/index.test.ts @@ -1,26 +1,20 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' // Skip on Turbopack because it's not supported ;(process.env.IS_TURBOPACK_TEST ? describe.skip : describe)( 'webpack-issuer-deprecation-warning', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` export default function Page() { return

hello world } `, - }, - dependencies: {}, - }) + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('should not appear deprecation warning about webpack module issuer', async () => { const html = await renderViaHTTP(next.url, '/') diff --git a/test/e2e/404-page-router/index.test.ts b/test/e2e/404-page-router/index.test.ts index e5bff4720f42..61ff69bb9684 100644 --- a/test/e2e/404-page-router/index.test.ts +++ b/test/e2e/404-page-router/index.test.ts @@ -1,6 +1,6 @@ import path from 'path' import fs from 'fs-extra' -import { createNext, FileRef, type NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { check, retry } from 'next-test-utils' const pathnames = { @@ -35,16 +35,14 @@ module.exports = { ` describe('404-page-router', () => { - let next: NextInstance - - beforeAll(async () => { - const files = { + const { next } = nextTestSetup({ + files: { pages: new FileRef(path.join(__dirname, 'app/pages')), components: new FileRef(path.join(__dirname, 'app/components')), - } - next = await createNext({ files, skipStart: true, patchFileDelay: 500 }) + }, + skipStart: true, + patchFileDelay: 500, }) - afterAll(() => next.destroy()) describe.each(table)( '404-page-router with basePath of $basePath and i18n of $i18n and middleware $middleware', diff --git a/test/e2e/app-dir/app/useReportWebVitals.test.ts b/test/e2e/app-dir/app/useReportWebVitals.test.ts index 8865fbddb8d8..a28457b7e60a 100644 --- a/test/e2e/app-dir/app/useReportWebVitals.test.ts +++ b/test/e2e/app-dir/app/useReportWebVitals.test.ts @@ -1,23 +1,19 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { check } from 'next-test-utils' describe('useReportWebVitals hook', () => { - let next: NextInstance + const { next } = nextTestSetup({ + files: __dirname, + skipStart: true, + env: {}, + dependencies: { + nanoid: '4.0.1', + }, + }) beforeAll(async () => { - next = await createNext({ - files: __dirname, - skipStart: true, - env: {}, - dependencies: { - nanoid: '4.0.1', - }, - }) - await next.start() }) - afterAll(() => next.destroy()) // Analytics events are only sent in production it('should send web-vitals', async () => { diff --git a/test/e2e/app-dir/catch-error/catch-error-react-compiler.test.ts b/test/e2e/app-dir/catch-error/catch-error-react-compiler.test.ts index 15fcaeb0bdea..753b299f09f0 100644 --- a/test/e2e/app-dir/catch-error/catch-error-react-compiler.test.ts +++ b/test/e2e/app-dir/catch-error/catch-error-react-compiler.test.ts @@ -1,9 +1,8 @@ -import { nextTestSetup } from 'e2e-utils' +import { isReact18, nextTestSetup } from 'e2e-utils' // FIXME: If NEXT_TEST_REACT_VERSION is set, skip the test for now. Need to address react/compiler-runtime // compatibility with React below 19. // _describe for cleaner git history. -const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 const _describe = isReact18 ? describe.skip : describe _describe('app-dir - unstable_catchError with react compiler', () => { diff --git a/test/e2e/app-dir/create-root-layout/create-root-layout.test.ts b/test/e2e/app-dir/create-root-layout/create-root-layout.test.ts index 2d395f6be1b8..a9d9ec19e141 100644 --- a/test/e2e/app-dir/create-root-layout/create-root-layout.test.ts +++ b/test/e2e/app-dir/create-root-layout/create-root-layout.test.ts @@ -1,6 +1,5 @@ import path from 'path' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { createNext, FileRef, nextTestSetup } from 'e2e-utils' import { check } from 'next-test-utils' import stripAnsi from 'strip-ansi' @@ -15,22 +14,17 @@ import stripAnsi from 'strip-ansi' return } - let next: NextInstance - if (isDev) { describe('page.js', () => { describe('root layout in app', () => { - beforeAll(async () => { - next = await createNext({ - files: { - app: new FileRef(path.join(__dirname, 'app')), - 'next.config.js': new FileRef( - path.join(__dirname, 'next.config.js') - ), - }, - }) + const { next } = nextTestSetup({ + files: { + app: new FileRef(path.join(__dirname, 'app')), + 'next.config.js': new FileRef( + path.join(__dirname, 'next.config.js') + ), + }, }) - afterAll(() => next.destroy()) it('create root layout', async () => { const outputIndex = next.cliOutput.length @@ -67,17 +61,14 @@ import stripAnsi from 'strip-ansi' }) describe('root layout in route group', () => { - beforeAll(async () => { - next = await createNext({ - files: { - app: new FileRef(path.join(__dirname, 'app-group-layout')), - 'next.config.js': new FileRef( - path.join(__dirname, 'next.config.js') - ), - }, - }) + const { next } = nextTestSetup({ + files: { + app: new FileRef(path.join(__dirname, 'app-group-layout')), + 'next.config.js': new FileRef( + path.join(__dirname, 'next.config.js') + ), + }, }) - afterAll(() => next.destroy()) it('create root layout', async () => { const outputIndex = next.cliOutput.length @@ -115,19 +106,14 @@ import stripAnsi from 'strip-ansi' }) describe('find available dir', () => { - beforeAll(async () => { - next = await createNext({ - files: { - app: new FileRef( - path.join(__dirname, 'app-find-available-dir') - ), - 'next.config.js': new FileRef( - path.join(__dirname, 'next.config.js') - ), - }, - }) + const { next } = nextTestSetup({ + files: { + app: new FileRef(path.join(__dirname, 'app-find-available-dir')), + 'next.config.js': new FileRef( + path.join(__dirname, 'next.config.js') + ), + }, }) - afterAll(() => next.destroy()) it('create root layout', async () => { const outputIndex = next.cliOutput.length @@ -166,19 +152,16 @@ import stripAnsi from 'strip-ansi' }) describe('page.tsx', () => { - beforeAll(async () => { - next = await createNext({ - files: { - 'app/page.tsx': new FileRef( - path.join(__dirname, 'app/route/page.js') - ), - 'next.config.js': new FileRef( - path.join(__dirname, 'next.config.js') - ), - }, - }) + const { next } = nextTestSetup({ + files: { + 'app/page.tsx': new FileRef( + path.join(__dirname, 'app/route/page.js') + ), + 'next.config.js': new FileRef( + path.join(__dirname, 'next.config.js') + ), + }, }) - afterAll(() => next.destroy()) it('create root layout', async () => { const outputIndex = next.cliOutput.length diff --git a/test/e2e/app-dir/interoperability-with-pages/navigation.test.ts b/test/e2e/app-dir/interoperability-with-pages/navigation.test.ts index 82010ebe1601..3288505eb5f1 100644 --- a/test/e2e/app-dir/interoperability-with-pages/navigation.test.ts +++ b/test/e2e/app-dir/interoperability-with-pages/navigation.test.ts @@ -1,21 +1,15 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import webdriver from 'next-webdriver' describe('navigation between pages and app dir', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef(__dirname), - dependencies: { - typescript: 'latest', - '@types/react': 'latest', - '@types/node': 'latest', - }, - }) + const { next } = nextTestSetup({ + files: new FileRef(__dirname), + dependencies: { + typescript: 'latest', + '@types/react': 'latest', + '@types/node': 'latest', + }, }) - afterAll(() => next.destroy()) it('It should be able to navigate app -> pages', async () => { const browser = await webdriver(next.url, '/app') diff --git a/test/e2e/app-dir/javascript-urls/javascript-urls.test.ts b/test/e2e/app-dir/javascript-urls/javascript-urls.test.ts index 78a523f3e3b5..c05f4acdd5ac 100644 --- a/test/e2e/app-dir/javascript-urls/javascript-urls.test.ts +++ b/test/e2e/app-dir/javascript-urls/javascript-urls.test.ts @@ -1,4 +1,4 @@ -import { nextTestSetup } from 'e2e-utils' +import { isReact18, nextTestSetup } from 'e2e-utils' import { getRedboxDescription, retry, @@ -7,8 +7,6 @@ import { } from 'next-test-utils' import type { Page, Request } from 'playwright' -const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 - describe('javascript-urls', () => { const { next, isNextDev } = nextTestSetup({ files: __dirname, diff --git a/test/e2e/app-dir/use-selected-layout-segment-s/use-selected-layout-segment-s.test.ts b/test/e2e/app-dir/use-selected-layout-segment-s/use-selected-layout-segment-s.test.ts index 370ebae5df37..2fc4de5f95cf 100644 --- a/test/e2e/app-dir/use-selected-layout-segment-s/use-selected-layout-segment-s.test.ts +++ b/test/e2e/app-dir/use-selected-layout-segment-s/use-selected-layout-segment-s.test.ts @@ -1,17 +1,11 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import webdriver from 'next-webdriver' import { check } from 'next-test-utils' describe('useSelectedLayoutSegment(s)', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef(__dirname), - }) + const { next } = nextTestSetup({ + files: new FileRef(__dirname), }) - afterAll(() => next.destroy()) let browser: Awaited> beforeEach(async () => { diff --git a/test/e2e/basepath/trailing-slash.test.ts b/test/e2e/basepath/trailing-slash.test.ts index 1f408c5472ce..8e7db8970b0a 100644 --- a/test/e2e/basepath/trailing-slash.test.ts +++ b/test/e2e/basepath/trailing-slash.test.ts @@ -1,26 +1,21 @@ import webdriver from 'next-webdriver' -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { waitForNoRedbox } from 'next-test-utils' describe('basePath + trailingSlash', () => { - let next: NextInstance const basePath = '/docs' - beforeAll(async () => { - next = await createNext({ - files: __dirname, - nextConfig: { - trailingSlash: true, - basePath, - onDemandEntries: { - // Make sure entries are not getting disposed. - maxInactiveAge: 1000 * 60 * 60, - }, + const { next } = nextTestSetup({ + files: __dirname, + nextConfig: { + trailingSlash: true, + basePath, + onDemandEntries: { + // Make sure entries are not getting disposed. + maxInactiveAge: 1000 * 60 * 60, }, - }) + }, }) - afterAll(() => next.destroy()) const runTests = (dev = false) => { it('should allow URL query strings without refresh', async () => { diff --git a/test/e2e/browserslist-extends/index.test.ts b/test/e2e/browserslist-extends/index.test.ts index 464f9afad27a..a58cdcfc4d75 100644 --- a/test/e2e/browserslist-extends/index.test.ts +++ b/test/e2e/browserslist-extends/index.test.ts @@ -1,35 +1,29 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' describe('browserslist-extends', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - import styles from './index.module.css' - - export default function Page() { - return

hello world

- } - `, - 'pages/index.module.css': ` - .hello { - color: pink; - } - `, - }, - dependencies: { - 'browserslist-config-google': '^3.0.1', - }, - packageJson: { - browserslist: ['extends browserslist-config-google'], - }, - }) + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + import styles from './index.module.css' + + export default function Page() { + return

hello world

+ } + `, + 'pages/index.module.css': ` + .hello { + color: pink; + } + `, + }, + dependencies: { + 'browserslist-config-google': '^3.0.1', + }, + packageJson: { + browserslist: ['extends browserslist-config-google'], + }, }) - afterAll(() => next.destroy()) it('should work', async () => { const html = await renderViaHTTP(next.url, '/') diff --git a/test/e2e/browserslist/browserslist.test.ts b/test/e2e/browserslist/browserslist.test.ts index ebdae5425b21..172870610d10 100644 --- a/test/e2e/browserslist/browserslist.test.ts +++ b/test/e2e/browserslist/browserslist.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { renderViaHTTP, fetchViaHTTP } from 'next-test-utils' import path from 'path' import cheerio from 'cheerio' @@ -7,18 +6,13 @@ const appDir = path.join(__dirname, 'app') // TODO: This test needs to check multiple files and syntax features. describe.skip('Browserslist', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(path.join(appDir, 'pages')), - // In Chrome 45 arrow functions are introduced, by - '.browserslistrc': 'Chrome 42', - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(path.join(appDir, 'pages')), + // In Chrome 45 arrow functions are introduced, by + '.browserslistrc': 'Chrome 42', + }, }) - afterAll(() => next.destroy()) it('should apply browserslist target', async () => { const html = await renderViaHTTP(next.url, '/') diff --git a/test/e2e/browserslist/default-target.test.ts b/test/e2e/browserslist/default-target.test.ts index b6a7f5f1e5a7..bcc9860fc488 100644 --- a/test/e2e/browserslist/default-target.test.ts +++ b/test/e2e/browserslist/default-target.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { renderViaHTTP, fetchViaHTTP } from 'next-test-utils' import path from 'path' import cheerio from 'cheerio' @@ -7,16 +6,11 @@ const appDir = path.join(__dirname, 'app') // TODO: This test needs to check multiple files and syntax features. describe.skip('default browserslist target', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(path.join(appDir, 'pages')), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(path.join(appDir, 'pages')), + }, }) - afterAll(() => next.destroy()) it('should apply default browserslist target', async () => { const html = await renderViaHTTP(next.url, '/') diff --git a/test/e2e/config-promise-export/async-function.test.ts b/test/e2e/config-promise-export/async-function.test.ts index a7d011b3eb69..864f0f094573 100644 --- a/test/e2e/config-promise-export/async-function.test.ts +++ b/test/e2e/config-promise-export/async-function.test.ts @@ -1,30 +1,24 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' describe('async export', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - export default function Page() { - return

hello world

- } - `, - 'next.config.js': ` - module.exports = async () => { - return { - basePath: '/docs' - } + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + export default function Page() { + return

hello world

+ } + `, + 'next.config.js': ` + module.exports = async () => { + return { + basePath: '/docs' } - `, - }, - dependencies: {}, - }) + } + `, + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('should work', async () => { const html = await renderViaHTTP(next.url, '/docs') diff --git a/test/e2e/config-promise-export/promise.test.ts b/test/e2e/config-promise-export/promise.test.ts index 6827ba95d56e..daa9fe40ea42 100644 --- a/test/e2e/config-promise-export/promise.test.ts +++ b/test/e2e/config-promise-export/promise.test.ts @@ -1,30 +1,24 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' describe('promise export', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - export default function Page() { - return

hello world

- } - `, - 'next.config.js': ` - module.exports = new Promise((resolve) => { - resolve({ - basePath: '/docs' - }) + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + export default function Page() { + return

hello world

+ } + `, + 'next.config.js': ` + module.exports = new Promise((resolve) => { + resolve({ + basePath: '/docs' }) - `, - }, - dependencies: {}, - }) + }) + `, + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('should work', async () => { const html = await renderViaHTTP(next.url, '/docs') diff --git a/test/e2e/edge-api-endpoints-can-receive-body/index.test.ts b/test/e2e/edge-api-endpoints-can-receive-body/index.test.ts index 93c049001ba3..d0174aa70255 100644 --- a/test/e2e/edge-api-endpoints-can-receive-body/index.test.ts +++ b/test/e2e/edge-api-endpoints-can-receive-body/index.test.ts @@ -1,25 +1,19 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { fetchViaHTTP } from 'next-test-utils' import path from 'path' describe('Edge API endpoints can receive body', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/api/edge.js': new FileRef( - path.resolve(__dirname, './app/pages/api/edge.js') - ), - 'pages/api/index.js': new FileRef( - path.resolve(__dirname, './app/pages/api/index.js') - ), - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + 'pages/api/edge.js': new FileRef( + path.resolve(__dirname, './app/pages/api/edge.js') + ), + 'pages/api/index.js': new FileRef( + path.resolve(__dirname, './app/pages/api/index.js') + ), + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('reads the body as text', async () => { const res = await fetchViaHTTP( diff --git a/test/e2e/edge-can-read-request-body/index.test.ts b/test/e2e/edge-can-read-request-body/index.test.ts index 347ccce75cfe..cd2e5e20d545 100644 --- a/test/e2e/edge-can-read-request-body/index.test.ts +++ b/test/e2e/edge-can-read-request-body/index.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils' import path from 'path' import type { Response } from 'node-fetch' @@ -13,15 +12,10 @@ async function serialize(response: Response) { } describe('Edge can read request body', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef(path.resolve(__dirname, './app')), - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: new FileRef(path.resolve(__dirname, './app')), + dependencies: {}, }) - afterAll(() => next.destroy()) it('renders the static page', async () => { const html = await renderViaHTTP(next.url, '/api/nothing') diff --git a/test/e2e/edge-can-use-wasm-files/index.test.ts b/test/e2e/edge-can-use-wasm-files/index.test.ts index a36645bd003b..a02aa4dce438 100644 --- a/test/e2e/edge-can-use-wasm-files/index.test.ts +++ b/test/e2e/edge-can-use-wasm-files/index.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { fetchViaHTTP } from 'next-test-utils' import path from 'path' import fs from 'fs-extra' @@ -8,7 +7,7 @@ function extractJSON(response) { return JSON.parse(response.headers.get('data') ?? '{}') } -function baseNextConfig(): Parameters[0] { +function baseNextConfig(): Parameters[0] { return { files: { 'src/add.wasm': new FileRef(path.join(__dirname, './add.wasm')), @@ -37,34 +36,30 @@ function baseNextConfig(): Parameters[0] { } describe('edge api endpoints can use wasm files', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/api/add.js': ` - import { increment } from '../../src/add.js' - export default async (request) => { - const input = Number(request.nextUrl.searchParams.get('input')) || 1; - const value = await increment(input); - return new Response(null, { headers: { data: JSON.stringify({ input, value }) } }); - } - export const config = { runtime: 'edge' }; - `, - 'src/add.wasm': new FileRef(path.join(__dirname, './add.wasm')), - 'src/add.js': ` - import wasm from './add.wasm?module' - const instance$ = WebAssembly.instantiate(wasm); - - export async function increment(a) { - const { exports } = await instance$; - return exports.add_one(a); - } - `, - }, - }) + const { next } = nextTestSetup({ + files: { + 'pages/api/add.js': ` + import { increment } from '../../src/add.js' + export default async (request) => { + const input = Number(request.nextUrl.searchParams.get('input')) || 1; + const value = await increment(input); + return new Response(null, { headers: { data: JSON.stringify({ input, value }) } }); + } + export const config = { runtime: 'edge' }; + `, + 'src/add.wasm': new FileRef(path.join(__dirname, './add.wasm')), + 'src/add.js': ` + import wasm from './add.wasm?module' + const instance$ = WebAssembly.instantiate(wasm); + + export async function increment(a) { + const { exports } = await instance$; + return exports.add_one(a); + } + `, + }, }) - afterAll(() => next.destroy()) + it('uses the wasm file', async () => { const response = await fetchViaHTTP(next.url, '/api/add', { input: 10 }) expect(extractJSON(response)).toEqual({ @@ -75,13 +70,7 @@ describe('edge api endpoints can use wasm files', () => { }) describe('middleware can use wasm files', () => { - let next: NextInstance - - beforeAll(async () => { - const config = baseNextConfig() - next = await createNext(config) - }) - afterAll(() => next.destroy()) + const { next } = nextTestSetup(baseNextConfig()) it('uses the wasm file', async () => { const response = await fetchViaHTTP(next.url, '/') @@ -134,25 +123,20 @@ describe('middleware can use wasm files', () => { }) describe('middleware can use wasm files with the experimental modes on', () => { - let next: NextInstance - - beforeAll(async () => { - const config = baseNextConfig() - config.files['next.config.js'] = ` - module.exports = { - webpack(config) { - config.output.webassemblyModuleFilename = 'static/wasm/[modulehash].wasm' + const config = baseNextConfig() + ;(config.files as Record)['next.config.js'] = ` + module.exports = { + webpack(config) { + config.output.webassemblyModuleFilename = 'static/wasm/[modulehash].wasm' - // Since Webpack 5 doesn't enable WebAssembly by default, we should do it manually - config.experiments = { ...config.experiments, asyncWebAssembly: true } + // Since Webpack 5 doesn't enable WebAssembly by default, we should do it manually + config.experiments = { ...config.experiments, asyncWebAssembly: true } - return config - }, - } - ` - next = await createNext(config) - }) - afterAll(() => next.destroy()) + return config + }, + } + ` + const { next } = nextTestSetup(config) it('uses the wasm file', async () => { const response = await fetchViaHTTP(next.url, '/') diff --git a/test/e2e/edge-compiler-module-exports-preference/index.test.ts b/test/e2e/edge-compiler-module-exports-preference/index.test.ts index d7fafeefbddb..c0e5110d65fa 100644 --- a/test/e2e/edge-compiler-module-exports-preference/index.test.ts +++ b/test/e2e/edge-compiler-module-exports-preference/index.test.ts @@ -1,10 +1,7 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { fetchViaHTTP } from 'next-test-utils' describe('Edge compiler module exports preference', () => { - let next: NextInstance - if ((global as any).isNextDeploy) { // this test is skipped when deployed because it manually creates a package in the node_modules directory // which is unsupported @@ -12,49 +9,46 @@ describe('Edge compiler module exports preference', () => { return } - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - export default function Page() { - return

hello world

- } - `, - 'middleware.js': ` - import { NextResponse } from 'next/server'; - import lib from 'my-lib'; + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + export default function Page() { + return

hello world

+ } + `, + 'middleware.js': ` + import { NextResponse } from 'next/server'; + import lib from 'my-lib'; - export default (req) => { - return NextResponse.next({ - headers: { - 'x-imported': lib - } - }) - } - `, - 'node_modules/my-lib/package.json': JSON.stringify({ - name: 'my-lib', - version: '1.0.0', - main: 'index.js', - browser: 'browser.js', - }), - 'node_modules/my-lib/index.js': `module.exports = "Node.js"`, - 'node_modules/my-lib/browser.js': `module.exports = "Browser"`, - }, - packageJson: { - scripts: { - build: 'next build', - dev: 'next dev', - start: 'next start', - }, + export default (req) => { + return NextResponse.next({ + headers: { + 'x-imported': lib + } + }) + } + `, + 'node_modules/my-lib/package.json': JSON.stringify({ + name: 'my-lib', + version: '1.0.0', + main: 'index.js', + browser: 'browser.js', + }), + 'node_modules/my-lib/index.js': `module.exports = "Node.js"`, + 'node_modules/my-lib/browser.js': `module.exports = "Browser"`, + }, + packageJson: { + scripts: { + build: 'next build', + dev: 'next dev', + start: 'next start', }, - installCommand: 'pnpm i', - startCommand: (global as any).isNextDev ? 'pnpm dev' : 'pnpm start', - buildCommand: 'pnpm run build', - dependencies: {}, - }) + }, + installCommand: 'pnpm i', + startCommand: (global as any).isNextDev ? 'pnpm dev' : 'pnpm start', + buildCommand: 'pnpm run build', + dependencies: {}, }) - afterAll(() => next.destroy()) it('favors the browser export', async () => { const response = await fetchViaHTTP(next.url, '/') diff --git a/test/e2e/getserversideprops/test/index.test.ts b/test/e2e/getserversideprops/test/index.test.ts index 63b2253d1bf9..bad1aaa03cf3 100644 --- a/test/e2e/getserversideprops/test/index.test.ts +++ b/test/e2e/getserversideprops/test/index.test.ts @@ -1,7 +1,7 @@ /* eslint-env jest */ import cheerio from 'cheerio' -import { createNext, FileRef } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import escapeRegex from 'escape-string-regexp' import { check, @@ -16,12 +16,10 @@ import { } from 'next-test-utils' import { join } from 'path' import webdriver from 'next-webdriver' -import { NextInstance } from 'e2e-utils' const appDir = join(__dirname, '../app') let buildId -let next: NextInstance const expectedManifestRoutes = () => [ { @@ -203,7 +201,7 @@ const expectedManifestRoutes = () => [ }, ] -const navigateTest = () => { +const navigateTest = (next: ReturnType['next']) => { it('should navigate between pages successfully', async () => { const toBuild = [ '/', @@ -289,8 +287,12 @@ const navigateTest = () => { }) } -const runTests = (isDev = false, isDeploy = false) => { - navigateTest() +const runTests = ( + next: ReturnType['next'], + isDev = false, + isDeploy = false +) => { + navigateTest(next) it('should work with early request ending', async () => { const res = await fetchViaHTTP(next.url, '/early-request-end') @@ -869,19 +871,18 @@ const runTests = (isDev = false, isDeploy = false) => { } describe('getServerSideProps', () => { - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(appDir, 'pages')), - public: new FileRef(join(appDir, 'public')), - 'world.txt': new FileRef(join(appDir, 'world.txt')), - 'next.config.js': new FileRef(join(appDir, 'next.config.js')), - }, - patchFileDelay: 500, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(appDir, 'pages')), + public: new FileRef(join(appDir, 'public')), + 'world.txt': new FileRef(join(appDir, 'world.txt')), + 'next.config.js': new FileRef(join(appDir, 'next.config.js')), + }, + patchFileDelay: 500, + }) + beforeAll(() => { buildId = next.buildId }) - afterAll(() => next.destroy()) - runTests((global as any).isNextDev, (global as any).isNextDeploy) + runTests(next, (global as any).isNextDev, (global as any).isNextDeploy) }) diff --git a/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts b/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts index 89059f33b388..18db69d87705 100644 --- a/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts +++ b/test/e2e/handle-non-hoisted-swc-helpers/index.test.ts @@ -1,45 +1,39 @@ -import { createNext, isNextDev } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { isNextDev, nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' describe('handle-non-hoisted-swc-helpers', () => { - let next: NextInstance + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + export default function Page() { + return

hello world

+ } - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - export default function Page() { - return

hello world

- } - - export function getServerSideProps() { - const helper = require('@swc/helpers/_/_object_spread') - console.log(helper) - return { - props: { - now: Date.now() - } + export function getServerSideProps() { + const helper = require('@swc/helpers/_/_object_spread') + console.log(helper) + return { + props: { + now: Date.now() } } - `, - }, - packageJson: { - packageManager: 'npm@10.9.2', - scripts: { - build: 'next build', - dev: 'next dev', - start: 'next start', - }, + } + `, + }, + packageJson: { + packageManager: 'npm@10.9.2', + scripts: { + build: 'next build', + dev: 'next dev', + start: 'next start', }, - installCommand: - 'npm install; mkdir -p node_modules/next/node_modules/@swc; mv node_modules/@swc/helpers node_modules/next/node_modules/@swc/', - buildCommand: 'npm run build', - startCommand: isNextDev ? 'npm run dev' : 'npm run start', - dependencies: {}, - }) + }, + installCommand: + 'npm install; mkdir -p node_modules/next/node_modules/@swc; mv node_modules/@swc/helpers node_modules/next/node_modules/@swc/', + buildCommand: 'npm run build', + startCommand: isNextDev ? 'npm run dev' : 'npm run start', + dependencies: {}, }) - afterAll(() => next.destroy()) it('should work', async () => { const html = await renderViaHTTP(next.url, '/') diff --git a/test/e2e/i18n-api-support/index.test.ts b/test/e2e/i18n-api-support/index.test.ts index 3e51e9b1ec4e..f2112bb25c8b 100644 --- a/test/e2e/i18n-api-support/index.test.ts +++ b/test/e2e/i18n-api-support/index.test.ts @@ -1,46 +1,40 @@ -import { createNext } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { fetchViaHTTP } from 'next-test-utils' -import { NextInstance } from 'e2e-utils' describe('i18n API support', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/api/hello.js': ` - export default function handler(req, res) { - res.end('hello world') - } - `, - 'pages/api/blog/[slug].js': ` - export default function handler(req, res) { - res.end('blog/[slug]') - } - `, + const { next } = nextTestSetup({ + files: { + 'pages/api/hello.js': ` + export default function handler(req, res) { + res.end('hello world') + } + `, + 'pages/api/blog/[slug].js': ` + export default function handler(req, res) { + res.end('blog/[slug]') + } + `, + }, + nextConfig: { + i18n: { + locales: ['en', 'fr'], + defaultLocale: 'en', }, - nextConfig: { - i18n: { - locales: ['en', 'fr'], - defaultLocale: 'en', - }, - async rewrites() { - return { - beforeFiles: [], - afterFiles: [], - fallback: [ - { - source: '/api/:path*', - destination: 'https://example.vercel.sh/', - }, - ], - } - }, + async rewrites() { + return { + beforeFiles: [], + afterFiles: [], + fallback: [ + { + source: '/api/:path*', + destination: 'https://example.vercel.sh/', + }, + ], + } }, - dependencies: {}, - }) + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('should respond to normal API request', async () => { const res = await fetchViaHTTP(next.url, '/api/hello') diff --git a/test/e2e/i18n-data-fetching-redirect/redirect-from-context.test.ts b/test/e2e/i18n-data-fetching-redirect/redirect-from-context.test.ts index d81e415f29ea..934d6b9c288c 100644 --- a/test/e2e/i18n-data-fetching-redirect/redirect-from-context.test.ts +++ b/test/e2e/i18n-data-fetching-redirect/redirect-from-context.test.ts @@ -1,28 +1,22 @@ import { join } from 'path' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { check } from 'next-test-utils' import webdriver from 'next-webdriver' describe('i18n-data-fetching-redirect', () => { - let next: NextInstance - // TODO: investigate tests failures on deploy if ((global as any).isNextDeploy) { it('should skip temporarily', () => {}) return } - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), - 'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')), - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'app/pages')), + 'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')), + }, + dependencies: {}, }) - afterAll(() => next.destroy()) describe('Redirect to locale from context', () => { test.each` diff --git a/test/e2e/i18n-data-fetching-redirect/redirect.test.ts b/test/e2e/i18n-data-fetching-redirect/redirect.test.ts index dc7074d94835..009c0a1bc2fa 100644 --- a/test/e2e/i18n-data-fetching-redirect/redirect.test.ts +++ b/test/e2e/i18n-data-fetching-redirect/redirect.test.ts @@ -1,28 +1,22 @@ import { join } from 'path' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { check } from 'next-test-utils' import webdriver from 'next-webdriver' describe('i18n-data-fetching-redirect', () => { - let next: NextInstance - // TODO: investigate tests failures on deploy if ((global as any).isNextDeploy) { it('should skip temporarily', () => {}) return } - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), - 'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')), - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'app/pages')), + 'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')), + }, + dependencies: {}, }) - afterAll(() => next.destroy()) describe('Redirect to another locale', () => { test.each` diff --git a/test/e2e/i18n-ignore-redirect-source-locale/redirects-with-basepath.test.ts b/test/e2e/i18n-ignore-redirect-source-locale/redirects-with-basepath.test.ts index c9c04a5ac88f..dee1d3ef300d 100644 --- a/test/e2e/i18n-ignore-redirect-source-locale/redirects-with-basepath.test.ts +++ b/test/e2e/i18n-ignore-redirect-source-locale/redirects-with-basepath.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { retry } from 'next-test-utils' import { join } from 'path' import webdriver from 'next-webdriver' @@ -7,52 +6,47 @@ import webdriver from 'next-webdriver' const locales = ['', '/en', '/sv', '/nl'] describe('i18n-ignore-redirect-source-locale with basepath', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'app/pages')), + }, + dependencies: {}, + nextConfig: { + basePath: '/basepath', + i18n: { + locales: ['en', 'sv', 'nl'], + defaultLocale: 'en', }, - dependencies: {}, - nextConfig: { - basePath: '/basepath', - i18n: { - locales: ['en', 'sv', 'nl'], - defaultLocale: 'en', - }, - async redirects() { - return [ - { - source: '/:locale/to-sv', - destination: '/sv/newpage', - permanent: false, - locale: false, - }, - { - source: '/:locale/to-en', - destination: '/en/newpage', - permanent: false, - locale: false, - }, - { - source: '/:locale/to-slash', - destination: '/newpage', - permanent: false, - locale: false, - }, - { - source: '/:locale/to-same', - destination: '/:locale/newpage', - permanent: false, - locale: false, - }, - ] - }, + async redirects() { + return [ + { + source: '/:locale/to-sv', + destination: '/sv/newpage', + permanent: false, + locale: false, + }, + { + source: '/:locale/to-en', + destination: '/en/newpage', + permanent: false, + locale: false, + }, + { + source: '/:locale/to-slash', + destination: '/newpage', + permanent: false, + locale: false, + }, + { + source: '/:locale/to-same', + destination: '/:locale/newpage', + permanent: false, + locale: false, + }, + ] }, - }) + }, }) - afterAll(() => next.destroy()) test.each(locales)( 'get redirected to the new page, from: %s to: sv', diff --git a/test/e2e/i18n-ignore-redirect-source-locale/redirects.test.ts b/test/e2e/i18n-ignore-redirect-source-locale/redirects.test.ts index f78c8302a904..5fb937bd3c32 100644 --- a/test/e2e/i18n-ignore-redirect-source-locale/redirects.test.ts +++ b/test/e2e/i18n-ignore-redirect-source-locale/redirects.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { check } from 'next-test-utils' import { join } from 'path' import webdriver from 'next-webdriver' @@ -7,51 +6,46 @@ import webdriver from 'next-webdriver' const locales = ['', '/en', '/sv', '/nl'] describe('i18n-ignore-redirect-source-locale', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'app/pages')), + }, + dependencies: {}, + nextConfig: { + i18n: { + locales: ['en', 'sv', 'nl'], + defaultLocale: 'en', }, - dependencies: {}, - nextConfig: { - i18n: { - locales: ['en', 'sv', 'nl'], - defaultLocale: 'en', - }, - async redirects() { - return [ - { - source: '/:locale/to-sv', - destination: '/sv/newpage', - permanent: false, - locale: false, - }, - { - source: '/:locale/to-en', - destination: '/en/newpage', - permanent: false, - locale: false, - }, - { - source: '/:locale/to-slash', - destination: '/newpage', - permanent: false, - locale: false, - }, - { - source: '/:locale/to-same', - destination: '/:locale/newpage', - permanent: false, - locale: false, - }, - ] - }, + async redirects() { + return [ + { + source: '/:locale/to-sv', + destination: '/sv/newpage', + permanent: false, + locale: false, + }, + { + source: '/:locale/to-en', + destination: '/en/newpage', + permanent: false, + locale: false, + }, + { + source: '/:locale/to-slash', + destination: '/newpage', + permanent: false, + locale: false, + }, + { + source: '/:locale/to-same', + destination: '/:locale/newpage', + permanent: false, + locale: false, + }, + ] }, - }) + }, }) - afterAll(() => next.destroy()) test.each(locales)( 'get redirected to the new page, from: %s to: sv', diff --git a/test/e2e/i18n-ignore-rewrite-source-locale/rewrites.test.ts b/test/e2e/i18n-ignore-rewrite-source-locale/rewrites.test.ts index c8e81da3b6c7..015e351b756e 100644 --- a/test/e2e/i18n-ignore-rewrite-source-locale/rewrites.test.ts +++ b/test/e2e/i18n-ignore-rewrite-source-locale/rewrites.test.ts @@ -1,5 +1,4 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { fetchViaHTTP, renderViaHTTP } from 'next-test-utils' import path from 'path' import fs from 'fs-extra' @@ -7,45 +6,40 @@ import fs from 'fs-extra' const locales = ['', '/en', '/sv', '/nl'] describe('i18n-ignore-rewrite-source-locale', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/api/hello.js': ` - export default function handler(req, res) { - res.send('hello from api') - }`, - 'public/file.txt': 'hello from file.txt', + const { next } = nextTestSetup({ + files: { + 'pages/api/hello.js': ` + export default function handler(req, res) { + res.send('hello from api') + }`, + 'public/file.txt': 'hello from file.txt', + }, + dependencies: {}, + nextConfig: { + i18n: { + locales: ['en', 'sv', 'nl'], + defaultLocale: 'en', }, - dependencies: {}, - nextConfig: { - i18n: { - locales: ['en', 'sv', 'nl'], - defaultLocale: 'en', - }, - async rewrites() { - return { - beforeFiles: [ - { - source: '/:locale/rewrite-files/:path*', - destination: '/:path*', - locale: false, - }, - { - source: '/:locale/rewrite-api/:path*', - destination: '/api/:path*', - locale: false, - }, - ], - afterFiles: [], - fallback: [], - } - }, + async rewrites() { + return { + beforeFiles: [ + { + source: '/:locale/rewrite-files/:path*', + destination: '/:path*', + locale: false, + }, + { + source: '/:locale/rewrite-api/:path*', + destination: '/api/:path*', + locale: false, + }, + ], + afterFiles: [], + fallback: [], + } }, - }) + }, }) - afterAll(() => next.destroy()) test.each(locales)( 'get public file by skipping locale in rewrite, locale: %s', diff --git a/test/e2e/ignore-invalid-popstateevent/with-i18n.test.ts b/test/e2e/ignore-invalid-popstateevent/with-i18n.test.ts index dff84317e55d..3454a4d5c928 100644 --- a/test/e2e/ignore-invalid-popstateevent/with-i18n.test.ts +++ b/test/e2e/ignore-invalid-popstateevent/with-i18n.test.ts @@ -1,6 +1,5 @@ import { join } from 'path' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { check, waitFor } from 'next-test-utils' import webdriver, { Playwright } from 'next-webdriver' @@ -14,18 +13,13 @@ const emitPopsStateEvent = (browser: Playwright, state: HistoryState) => ) describe('i18n: Event with stale state - static route previously was dynamic', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), - 'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')), - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'app/pages')), + 'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')), + }, + dependencies: {}, }) - afterAll(() => next.destroy()) test('Ignore event without query param', async () => { const browser = await webdriver(next.url, '/sv/static') diff --git a/test/e2e/ignore-invalid-popstateevent/without-i18n.test.ts b/test/e2e/ignore-invalid-popstateevent/without-i18n.test.ts index e981eb73e87c..a8c174172453 100644 --- a/test/e2e/ignore-invalid-popstateevent/without-i18n.test.ts +++ b/test/e2e/ignore-invalid-popstateevent/without-i18n.test.ts @@ -1,6 +1,5 @@ import { join } from 'path' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { check, waitFor } from 'next-test-utils' import webdriver, { Playwright } from 'next-webdriver' @@ -14,18 +13,13 @@ const emitPopsStateEvent = (browser: Playwright, state: HistoryState) => ) describe('Event with stale state - static route previously was dynamic', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), - // Don't use next.config.js to avoid getting i18n - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'app/pages')), + // Don't use next.config.js to avoid getting i18n + }, + dependencies: {}, }) - afterAll(() => next.destroy()) test('Ignore event without query param', async () => { const browser = await webdriver(next.url, '/static') diff --git a/test/e2e/link-with-api-rewrite/index.test.ts b/test/e2e/link-with-api-rewrite/index.test.ts index 955987ee4f39..70e4e0f6a1ca 100644 --- a/test/e2e/link-with-api-rewrite/index.test.ts +++ b/test/e2e/link-with-api-rewrite/index.test.ts @@ -1,22 +1,16 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { retry } from 'next-test-utils' import { join } from 'path' import webdriver from 'next-webdriver' describe('link-with-api-rewrite', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), - 'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')), - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'app/pages')), + 'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')), + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('should perform hard navigation for rewritten urls', async () => { const browser = await webdriver(next.url, '/') diff --git a/test/e2e/manual-client-base-path/index.test.ts b/test/e2e/manual-client-base-path/index.test.ts index 665a62494979..ff07b77423bc 100644 --- a/test/e2e/manual-client-base-path/index.test.ts +++ b/test/e2e/manual-client-base-path/index.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import httpProxy from 'http-proxy' import { join } from 'path' import http from 'http' @@ -13,20 +12,20 @@ describe('manual-client-base-path', () => { return } - let next: NextInstance let server: http.Server let appPort: string const basePath = '/docs-proxy' const responses = new Set() + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'app/pages')), + 'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')), + }, + dependencies: {}, + }) + beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), - 'next.config.js': new FileRef(join(__dirname, 'app/next.config.js')), - }, - dependencies: {}, - }) const getProxyTarget = (req) => { const destination = new URL(next.url) const reqUrl = new URL(req.url, 'http://localhost') @@ -89,7 +88,6 @@ describe('manual-client-base-path', () => { appPort = server.address().port }) afterAll(async () => { - await next.destroy() try { server.close() responses.forEach((res: any) => res.end?.() || res.close?.()) diff --git a/test/e2e/middleware-base-path/test/index.test.ts b/test/e2e/middleware-base-path/test/index.test.ts index af39eae4b441..6b204f701af8 100644 --- a/test/e2e/middleware-base-path/test/index.test.ts +++ b/test/e2e/middleware-base-path/test/index.test.ts @@ -3,22 +3,16 @@ import { join } from 'path' import cheerio from 'cheerio' import webdriver from 'next-webdriver' import { fetchViaHTTP } from 'next-test-utils' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' describe('Middleware base tests', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, '../app/pages')), - 'middleware.js': new FileRef(join(__dirname, '../app/middleware.js')), - 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, '../app/pages')), + 'middleware.js': new FileRef(join(__dirname, '../app/middleware.js')), + 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), + }, }) - afterAll(() => next.destroy()) it('should execute from absolute paths', async () => { const browser = await webdriver(next.url, '/redirect-with-basepath') diff --git a/test/e2e/middleware-custom-matchers-basepath/test/index.test.ts b/test/e2e/middleware-custom-matchers-basepath/test/index.test.ts index 5adeb7c5e0dc..ead5c889b9a2 100644 --- a/test/e2e/middleware-custom-matchers-basepath/test/index.test.ts +++ b/test/e2e/middleware-custom-matchers-basepath/test/index.test.ts @@ -4,22 +4,16 @@ import { join } from 'path' import webdriver from 'next-webdriver' import { fetchViaHTTP } from 'next-test-utils' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' const itif = (condition: boolean) => (condition ? it : it.skip) const isModeDeploy = process.env.NEXT_TEST_MODE === 'deploy' describe('Middleware custom matchers basePath', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef(join(__dirname, '../app')), - }) + const { next } = nextTestSetup({ + files: new FileRef(join(__dirname, '../app')), }) - afterAll(() => next.destroy()) // FIXME // See https://linear.app/vercel/issue/EC-170/middleware-rewrite-of-nextjs-with-basepath-does-not-work-on-vercel diff --git a/test/e2e/middleware-custom-matchers/test/index.test.ts b/test/e2e/middleware-custom-matchers/test/index.test.ts index 043ad2263af3..666ed91bcf56 100644 --- a/test/e2e/middleware-custom-matchers/test/index.test.ts +++ b/test/e2e/middleware-custom-matchers/test/index.test.ts @@ -2,33 +2,27 @@ import { join } from 'path' import webdriver from 'next-webdriver' import { fetchViaHTTP } from 'next-test-utils' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' const itif = (condition: boolean) => (condition ? it : it.skip) const isModeDeploy = process.env.NEXT_TEST_MODE === 'deploy' describe('Middleware custom matchers', () => { - let next: NextInstance - if ((global as any).isNextDeploy && process.env.TEST_NODE_MIDDLEWARE) { return it('should skip deploy for now', () => {}) } - beforeAll(async () => { - next = await createNext({ - files: new FileRef(join(__dirname, '../app')), - overrideFiles: process.env.TEST_NODE_MIDDLEWARE - ? { - 'middleware.js': new FileRef( - join(__dirname, '../app/middleware-node.js') - ), - } - : {}, - }) + const { next } = nextTestSetup({ + files: new FileRef(join(__dirname, '../app')), + overrideFiles: process.env.TEST_NODE_MIDDLEWARE + ? { + 'middleware.js': new FileRef( + join(__dirname, '../app/middleware-node.js') + ), + } + : {}, }) - afterAll(() => next.destroy()) const runTests = () => { it('should match missing header correctly', async () => { diff --git a/test/e2e/middleware-dynamic-basepath-matcher/test/index.test.ts b/test/e2e/middleware-dynamic-basepath-matcher/test/index.test.ts index ac63758c5c3b..b872391a9c2b 100644 --- a/test/e2e/middleware-dynamic-basepath-matcher/test/index.test.ts +++ b/test/e2e/middleware-dynamic-basepath-matcher/test/index.test.ts @@ -4,22 +4,16 @@ import { join } from 'path' import webdriver from 'next-webdriver' import { check, fetchViaHTTP } from 'next-test-utils' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' const itif = (condition: boolean) => (condition ? it : it.skip) const isModeDeploy = process.env.NEXT_TEST_MODE === 'deploy' describe('Middleware custom matchers basePath', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef(join(__dirname, '../app')), - }) + const { next } = nextTestSetup({ + files: new FileRef(join(__dirname, '../app')), }) - afterAll(() => next.destroy()) // FIXME // See https://linear.app/vercel/issue/EC-170/middleware-rewrite-of-nextjs-with-basepath-does-not-work-on-vercel diff --git a/test/e2e/middleware-fetches-with-any-http-method/index.test.ts b/test/e2e/middleware-fetches-with-any-http-method/index.test.ts index fc6fd23b3a7c..612bd8329335 100644 --- a/test/e2e/middleware-fetches-with-any-http-method/index.test.ts +++ b/test/e2e/middleware-fetches-with-any-http-method/index.test.ts @@ -1,51 +1,45 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { fetchViaHTTP } from 'next-test-utils' describe('Middleware fetches with any HTTP method', () => { - let next: NextInstance + const { next } = nextTestSetup({ + files: { + 'pages/api/ping.js': ` + export default (req, res) => { + res.send(JSON.stringify({ + method: req.method, + headers: {...req.headers}, + })) + } + `, + 'middleware.js': ` + import { NextResponse } from 'next/server'; - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/api/ping.js': ` - export default (req, res) => { - res.send(JSON.stringify({ - method: req.method, - headers: {...req.headers}, - })) - } - `, - 'middleware.js': ` - import { NextResponse } from 'next/server'; + const HTTP_ECHO_URL = 'https://next-data-api-endpoint.vercel.app/api/echo-headers'; - const HTTP_ECHO_URL = 'https://next-data-api-endpoint.vercel.app/api/echo-headers'; + export default async (req) => { + const kind = req.nextUrl.searchParams.get('kind') + const handler = handlers[kind] ?? handlers['normal-fetch']; - export default async (req) => { - const kind = req.nextUrl.searchParams.get('kind') - const handler = handlers[kind] ?? handlers['normal-fetch']; + const response = await handler({url: HTTP_ECHO_URL, method: req.method}); + const json = await response.text() - const response = await handler({url: HTTP_ECHO_URL, method: req.method}); - const json = await response.text() + const res = NextResponse.next(); + res.headers.set('x-resolved', json ?? '{}'); + return res + } - const res = NextResponse.next(); - res.headers.set('x-resolved', json ?? '{}'); - return res - } + const handlers = { + 'new-request': ({url, method}) => + fetch(new Request(url, { method, headers: { 'x-kind': 'new-request' } })), - const handlers = { - 'new-request': ({url, method}) => - fetch(new Request(url, { method, headers: { 'x-kind': 'new-request' } })), - - 'normal-fetch': ({url, method}) => - fetch(url, { method, headers: { 'x-kind': 'normal-fetch' } }) - } - `, - }, - dependencies: {}, - }) + 'normal-fetch': ({url, method}) => + fetch(url, { method, headers: { 'x-kind': 'normal-fetch' } }) + } + `, + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('passes the method on a direct fetch request', async () => { const response = await fetchViaHTTP( diff --git a/test/e2e/middleware-fetches-with-body/index.test.ts b/test/e2e/middleware-fetches-with-body/index.test.ts index 0496023171cd..7c045e2ae1fc 100644 --- a/test/e2e/middleware-fetches-with-body/index.test.ts +++ b/test/e2e/middleware-fetches-with-body/index.test.ts @@ -1,52 +1,46 @@ -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { fetchViaHTTP } from 'next-test-utils' describe('Middleware fetches with body', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/api/default.js': ` - export default (req, res) => res.json({ body: req.body }) - `, - 'pages/api/size_limit_5kb.js': ` - export const config = { api: { bodyParser: { sizeLimit: '5kb' } } } - export default (req, res) => res.json({ body: req.body }) - `, - 'pages/api/size_limit_5mb.js': ` - export const config = { api: { bodyParser: { sizeLimit: '5mb' } } } - export default (req, res) => res.json({ body: req.body }) - `, - 'pages/api/body_parser_false.js': ` - export const config = { api: { bodyParser: false } } - - async function buffer(readable) { - const chunks = [] - for await (const chunk of readable) { - chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk) - } - return Buffer.concat(chunks) + const { next } = nextTestSetup({ + files: { + 'pages/api/default.js': ` + export default (req, res) => res.json({ body: req.body }) + `, + 'pages/api/size_limit_5kb.js': ` + export const config = { api: { bodyParser: { sizeLimit: '5kb' } } } + export default (req, res) => res.json({ body: req.body }) + `, + 'pages/api/size_limit_5mb.js': ` + export const config = { api: { bodyParser: { sizeLimit: '5mb' } } } + export default (req, res) => res.json({ body: req.body }) + `, + 'pages/api/body_parser_false.js': ` + export const config = { api: { bodyParser: false } } + + async function buffer(readable) { + const chunks = [] + for await (const chunk of readable) { + chunks.push(typeof chunk === 'string' ? Buffer.from(chunk) : chunk) } + return Buffer.concat(chunks) + } - export default async (req, res) => { - const buf = await buffer(req) - const rawBody = buf.toString('utf8'); + export default async (req, res) => { + const buf = await buffer(req) + const rawBody = buf.toString('utf8'); - res.json({ rawBody, body: req.body }) - } - `, - 'middleware.js': ` - import { NextResponse } from 'next/server'; - - export default async (req) => NextResponse.next(); - `, - }, - dependencies: {}, - }) + res.json({ rawBody, body: req.body }) + } + `, + 'middleware.js': ` + import { NextResponse } from 'next/server'; + + export default async (req) => NextResponse.next(); + `, + }, + dependencies: {}, }) - afterAll(() => next.destroy()) describe('with default bodyParser sizeLimit (1mb)', () => { it('should return 413 for body greater than 1mb', async () => { diff --git a/test/e2e/middleware-general/test/index.test.ts b/test/e2e/middleware-general/test/index.test.ts index 4d75e245bf6f..279d1a9396d7 100644 --- a/test/e2e/middleware-general/test/index.test.ts +++ b/test/e2e/middleware-general/test/index.test.ts @@ -3,95 +3,87 @@ import fs from 'fs-extra' import { join } from 'path' import webdriver from 'next-webdriver' -import { isNextStart, NextInstance } from 'e2e-utils' +import { FileRef, isNextStart, nextTestSetup } from 'e2e-utils' import { check, fetchViaHTTP, waitFor } from 'next-test-utils' -import { createNext, FileRef } from 'e2e-utils' const urlsError = 'Please use only absolute URLs' describe('Middleware Runtime', () => { - let next: NextInstance - const isNodeMiddleware = Boolean(process.env.TEST_NODE_MIDDLEWARE) const setup = ({ i18n }: { i18n: boolean }) => { - afterAll(async () => { - await next.destroy() - }) - beforeAll(async () => { - next = await createNext({ - files: { - 'middleware.js': new FileRef( - join( - __dirname, - '../app', - isNodeMiddleware ? 'middleware-node.js' : 'middleware.js' - ) - ), - lib: new FileRef(join(__dirname, '../app/lib')), - pages: new FileRef(join(__dirname, '../app/pages')), - 'shared-package': new FileRef( - join(__dirname, '../app/node_modules/shared-package') - ), + return nextTestSetup({ + files: { + 'middleware.js': new FileRef( + join( + __dirname, + '../app', + isNodeMiddleware ? 'middleware-node.js' : 'middleware.js' + ) + ), + lib: new FileRef(join(__dirname, '../app/lib')), + pages: new FileRef(join(__dirname, '../app/pages')), + 'shared-package': new FileRef( + join(__dirname, '../app/node_modules/shared-package') + ), + }, + nextConfig: { + experimental: { + webpackBuildWorker: true, }, - nextConfig: { - experimental: { - webpackBuildWorker: true, - }, - ...(i18n - ? { - i18n: { - locales: ['en', 'fr', 'nl'], - defaultLocale: 'en', - }, - } - : {}), - async redirects() { - return [ - { - source: '/redirect-1', - destination: '/somewhere/else', - permanent: false, - }, - ] - }, - async rewrites() { - return [ - { - source: '/rewrite-1', - destination: '/ssr-page?from=config', + ...(i18n + ? { + i18n: { + locales: ['en', 'fr', 'nl'], + defaultLocale: 'en', }, - { - source: '/rewrite-2', - destination: '/about/a?from=next-config', - }, - { - source: '/sha', - destination: '/shallow', - }, - { - source: '/rewrite-3', - destination: '/blog/middleware-rewrite?hello=config', - }, - ] - }, + } + : {}), + async redirects() { + return [ + { + source: '/redirect-1', + destination: '/somewhere/else', + permanent: false, + }, + ] }, - packageJson: { - scripts: { - setup: `cp -r ./shared-package ./node_modules`, - build: 'pnpm run setup && next build', - dev: 'pnpm run setup && next dev', - start: 'next start', - }, + async rewrites() { + return [ + { + source: '/rewrite-1', + destination: '/ssr-page?from=config', + }, + { + source: '/rewrite-2', + destination: '/about/a?from=next-config', + }, + { + source: '/sha', + destination: '/shallow', + }, + { + source: '/rewrite-3', + destination: '/blog/middleware-rewrite?hello=config', + }, + ] }, - startCommand: (global as any).isNextDev ? 'pnpm dev' : 'pnpm start', - buildCommand: 'pnpm build', - env: { - ANOTHER_MIDDLEWARE_TEST: 'asdf2', - STRING_ENV_VAR: 'asdf3', - MIDDLEWARE_TEST: 'asdf', + }, + packageJson: { + scripts: { + setup: `cp -r ./shared-package ./node_modules`, + build: 'pnpm run setup && next build', + dev: 'pnpm run setup && next dev', + start: 'next start', }, - }) + }, + startCommand: (global as any).isNextDev ? 'pnpm dev' : 'pnpm start', + buildCommand: 'pnpm build', + env: { + ANOTHER_MIDDLEWARE_TEST: 'asdf2', + STRING_ENV_VAR: 'asdf3', + MIDDLEWARE_TEST: 'asdf', + }, }) } @@ -103,7 +95,10 @@ describe('Middleware Runtime', () => { return response.headers.get('error') } - function runTests({ i18n }: { i18n?: boolean }) { + function runTests( + next: ReturnType['next'], + { i18n }: { i18n?: boolean } + ) { it('should not treat as _next/data request with just header', async () => { const res = await next.fetch('/redirect-to-somewhere', { redirect: 'manual', @@ -857,12 +852,12 @@ describe('Middleware Runtime', () => { }) } describe('with i18n', () => { - setup({ i18n: true }) - runTests({ i18n: true }) + const { next } = setup({ i18n: true }) + runTests(next, { i18n: true }) }) describe('without i18n', () => { - setup({ i18n: false }) - runTests({ i18n: false }) + const { next } = setup({ i18n: false }) + runTests(next, { i18n: false }) }) }) diff --git a/test/e2e/middleware-matcher/index.test.ts b/test/e2e/middleware-matcher/index.test.ts index deeda042e67c..f10d77494018 100644 --- a/test/e2e/middleware-matcher/index.test.ts +++ b/test/e2e/middleware-matcher/index.test.ts @@ -1,19 +1,13 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { check, fetchViaHTTP } from 'next-test-utils' import { join } from 'path' import webdriver from 'next-webdriver' describe('Middleware can set the matcher in its config', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: new FileRef(join(__dirname, 'app')), - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: new FileRef(join(__dirname, 'app')), + dependencies: {}, }) - afterAll(() => next.destroy()) it('does add the header for root request', async () => { const response = await fetchViaHTTP(next.url, '/') @@ -226,42 +220,38 @@ describe('Middleware can set the matcher in its config', () => { }) describe('using a single matcher', () => { - let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/[...route].js': ` - export default function Page({ message }) { - return
-

root page

-

{message}

-
- } + const { next } = nextTestSetup({ + files: { + 'pages/[...route].js': ` + export default function Page({ message }) { + return
+

root page

+

{message}

+
+ } - export const getServerSideProps = ({ params }) => { - return { - props: { - message: "Hello from /" + params.route.join("/") - } + export const getServerSideProps = ({ params }) => { + return { + props: { + message: "Hello from /" + params.route.join("/") } } - `, - 'middleware.js': ` - import { NextResponse } from 'next/server' - export const config = { - matcher: '/middleware/works' - }; - export default (req) => { - const res = NextResponse.next(); - res.headers.set('X-From-Middleware', 'true'); - return res; - } - `, - }, - dependencies: {}, - }) + } + `, + 'middleware.js': ` + import { NextResponse } from 'next/server' + export const config = { + matcher: '/middleware/works' + }; + export default (req) => { + const res = NextResponse.next(); + res.headers.set('X-From-Middleware', 'true'); + return res; + } + `, + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('does not add the header for root request', async () => { const response = await fetchViaHTTP(next.url, '/') @@ -325,50 +315,46 @@ describe.each([ ])( 'using a single matcher with i18n for a non-root route$title', ({ trailingSlash }) => { - let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/[...route].js': ` - export default function Page({ message }) { - return
-

catchall page

-

{message}

-
- } + const { next } = nextTestSetup({ + files: { + 'pages/[...route].js': ` + export default function Page({ message }) { + return
+

catchall page

+

{message}

+
+ } - export const getServerSideProps = ({ params, locale }) => ({ - props: { - message: \`(\${locale}) Hello from /\${params.route.join("/")}\` - } - }) - `, - 'middleware.js': ` - import { NextResponse } from 'next/server' - export const config = { - matcher: '/middleware/works' - }; - export default (req) => { - const res = NextResponse.next(); - res.headers.set('X-From-Middleware', 'true'); - return res; + export const getServerSideProps = ({ params, locale }) => ({ + props: { + message: \`(\${locale}) Hello from /\${params.route.join("/")}\` } - `, - 'next.config.js': ` - module.exports = { - ${trailingSlash ? 'trailingSlash: true,' : ''} - i18n: { - localeDetection: false, - locales: ['es', 'en'], - defaultLocale: 'en', - } + }) + `, + 'middleware.js': ` + import { NextResponse } from 'next/server' + export const config = { + matcher: '/middleware/works' + }; + export default (req) => { + const res = NextResponse.next(); + res.headers.set('X-From-Middleware', 'true'); + return res; + } + `, + 'next.config.js': ` + module.exports = { + ${trailingSlash ? 'trailingSlash: true,' : ''} + i18n: { + localeDetection: false, + locales: ['es', 'en'], + defaultLocale: 'en', } - `, - }, - dependencies: {}, - }) + } + `, + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('adds the header for matched paths', async () => { const res1 = await fetchViaHTTP(next.url, '/middleware/works') @@ -426,38 +412,34 @@ describe.each([ ) describe('using root matcher', () => { - let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - export function getStaticProps() { - return { - props: { - message: 'hello world' - } + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + export function getStaticProps() { + return { + props: { + message: 'hello world' } } - - export default function Home({ message }) { - return
Hi there!
- } - `, - 'middleware.js': ` - import { NextResponse } from 'next/server' - export default (req) => { - const res = NextResponse.next(); - res.headers.set('X-From-Middleware', 'true'); - return res; - } + } - export const config = { matcher: '/' }; - `, - }, - dependencies: {}, - }) + export default function Home({ message }) { + return
Hi there!
+ } + `, + 'middleware.js': ` + import { NextResponse } from 'next/server' + export default (req) => { + const res = NextResponse.next(); + res.headers.set('X-From-Middleware', 'true'); + return res; + } + + export const config = { matcher: '/' }; + `, + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('adds the header to the /', async () => { const response = await fetchViaHTTP(next.url, '/') @@ -507,55 +489,51 @@ describe.each([ { title: '' }, { title: ' and trailingSlash', trailingSlash: true }, ])('using a single matcher with i18n$title', ({ trailingSlash }) => { - let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` - export default function Page({ message }) { - return
-

{message}

-
- } - export const getServerSideProps = ({ params, locale }) => ({ - props: { message: \`(\${locale}) Hello from /\` } - }) - `, - 'pages/[...route].js': ` - export default function Page({ message }) { - return
-

catchall page

-

{message}

-
- } - export const getServerSideProps = ({ params, locale }) => ({ - props: { message: \`(\${locale}) Hello from /\` + params.route.join("/") } - }) - `, - 'middleware.js': ` - import { NextResponse } from 'next/server' - export const config = { matcher: '/' }; - export default (req) => { - const res = NextResponse.next(); - res.headers.set('X-From-Middleware', 'true'); - return res; - } - `, - 'next.config.js': ` - module.exports = { - ${trailingSlash ? 'trailingSlash: true,' : ''} - i18n: { - localeDetection: false, - locales: ['es', 'en'], - defaultLocale: 'en', - } + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` + export default function Page({ message }) { + return
+

{message}

+
+ } + export const getServerSideProps = ({ params, locale }) => ({ + props: { message: \`(\${locale}) Hello from /\` } + }) + `, + 'pages/[...route].js': ` + export default function Page({ message }) { + return
+

catchall page

+

{message}

+
+ } + export const getServerSideProps = ({ params, locale }) => ({ + props: { message: \`(\${locale}) Hello from /\` + params.route.join("/") } + }) + `, + 'middleware.js': ` + import { NextResponse } from 'next/server' + export const config = { matcher: '/' }; + export default (req) => { + const res = NextResponse.next(); + res.headers.set('X-From-Middleware', 'true'); + return res; + } + `, + 'next.config.js': ` + module.exports = { + ${trailingSlash ? 'trailingSlash: true,' : ''} + i18n: { + localeDetection: false, + locales: ['es', 'en'], + defaultLocale: 'en', } - `, - }, - dependencies: {}, - }) + } + `, + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it(`adds the header for a matched path`, async () => { const res1 = await fetchViaHTTP(next.url, `/`) @@ -610,11 +588,9 @@ describe.each([ ])( 'using a single matcher with i18n and basePath$title', ({ trailingSlash }) => { - let next: NextInstance - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/index.js': ` + const { next } = nextTestSetup({ + files: { + 'pages/index.js': ` export default function Page({ message }) { return

root page

@@ -625,7 +601,7 @@ describe.each([ props: { message: \`(\${locale}) Hello from /\` } }) `, - 'pages/[...route].js': ` + 'pages/[...route].js': ` export default function Page({ message }) { return

catchall page

@@ -636,7 +612,7 @@ describe.each([ props: { message: \`(\${locale}) Hello from /\` + params.route.join("/") } }) `, - 'middleware.js': ` + 'middleware.js': ` import { NextResponse } from 'next/server' export const config = { matcher: '/' }; export default (req) => { @@ -645,7 +621,7 @@ describe.each([ return res; } `, - 'next.config.js': ` + 'next.config.js': ` module.exports = { ${trailingSlash ? 'trailingSlash: true,' : ''} basePath: '/root', @@ -656,11 +632,9 @@ describe.each([ } } `, - }, - dependencies: {}, - }) + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it(`adds the header for a matched path`, async () => { const res1 = await fetchViaHTTP(next.url, `/root`) diff --git a/test/e2e/middleware-redirects/test/index.test.ts b/test/e2e/middleware-redirects/test/index.test.ts index a97a4b08e025..b78f26b0d1ad 100644 --- a/test/e2e/middleware-redirects/test/index.test.ts +++ b/test/e2e/middleware-redirects/test/index.test.ts @@ -4,29 +4,23 @@ import { join } from 'path' import cheerio from 'cheerio' import webdriver from 'next-webdriver' import { check, fetchViaHTTP } from 'next-test-utils' -import { NextInstance } from 'e2e-utils' -import { createNext, FileRef } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' describe('Middleware Redirect', () => { - let next: NextInstance - - afterAll(() => next.destroy()) - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, '../app/pages')), - ...(process.env.TEST_NODE_MIDDLEWARE - ? { - 'proxy.js': new FileRef(join(__dirname, '../app/middleware.js')), - } - : { - 'middleware.js': new FileRef( - join(__dirname, '../app/middleware.js') - ), - }), - 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, '../app/pages')), + ...(process.env.TEST_NODE_MIDDLEWARE + ? { + 'proxy.js': new FileRef(join(__dirname, '../app/middleware.js')), + } + : { + 'middleware.js': new FileRef( + join(__dirname, '../app/middleware.js') + ), + }), + 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), + }, }) function tests() { it('should redirect correctly with redirect in next.config.js', async () => { diff --git a/test/e2e/middleware-request-header-overrides/test/index.test.ts b/test/e2e/middleware-request-header-overrides/test/index.test.ts index 42a946f76a1b..ce7209edfb30 100644 --- a/test/e2e/middleware-request-header-overrides/test/index.test.ts +++ b/test/e2e/middleware-request-header-overrides/test/index.test.ts @@ -1,24 +1,18 @@ /* eslint-env jest */ import { join } from 'path' -import { NextInstance } from 'e2e-utils' import { fetchViaHTTP } from 'next-test-utils' -import { createNext, FileRef } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import cheerio from 'cheerio' import type { Response } from 'node-fetch' describe('Middleware Request Headers Overrides', () => { - let next: NextInstance - - afterAll(() => next.destroy()) - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, '../app/pages')), - 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), - 'middleware.js': new FileRef(join(__dirname, '../app/middleware.js')), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, '../app/pages')), + 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), + 'middleware.js': new FileRef(join(__dirname, '../app/middleware.js')), + }, }) describe.each([ diff --git a/test/e2e/middleware-responses/test/index.test.ts b/test/e2e/middleware-responses/test/index.test.ts index 6bd5c61f53eb..09cad9c4aea8 100644 --- a/test/e2e/middleware-responses/test/index.test.ts +++ b/test/e2e/middleware-responses/test/index.test.ts @@ -2,21 +2,15 @@ import { join } from 'path' import { fetchViaHTTP } from 'next-test-utils' -import { NextInstance } from 'e2e-utils' -import { createNext, FileRef } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' describe('Middleware Responses', () => { - let next: NextInstance - - afterAll(() => next.destroy()) - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, '../app/pages')), - 'middleware.js': new FileRef(join(__dirname, '../app/middleware.js')), - 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, '../app/pages')), + 'middleware.js': new FileRef(join(__dirname, '../app/middleware.js')), + 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), + }, }) function testsWithLocale(locale = '') { const label = locale ? `${locale} ` : `` diff --git a/test/e2e/middleware-rewrites/test/index.test.ts b/test/e2e/middleware-rewrites/test/index.test.ts index 01c67e9d2c48..83c2ef5b2588 100644 --- a/test/e2e/middleware-rewrites/test/index.test.ts +++ b/test/e2e/middleware-rewrites/test/index.test.ts @@ -3,23 +3,17 @@ import { join } from 'path' import cheerio from 'cheerio' import webdriver from 'next-webdriver' -import { NextInstance } from 'e2e-utils' import { check, fetchViaHTTP, retry } from 'next-test-utils' -import { createNext, FileRef } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import escapeStringRegexp from 'escape-string-regexp' describe('Middleware Rewrite', () => { - let next: NextInstance - - afterAll(() => next.destroy()) - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, '../app/pages')), - 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), - 'middleware.js': new FileRef(join(__dirname, '../app/middleware.js')), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, '../app/pages')), + 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), + 'middleware.js': new FileRef(join(__dirname, '../app/middleware.js')), + }, }) function tests() { diff --git a/test/e2e/middleware-shallow-link/index.test.ts b/test/e2e/middleware-shallow-link/index.test.ts index ff47247e9f8e..29719b0b5cb9 100644 --- a/test/e2e/middleware-shallow-link/index.test.ts +++ b/test/e2e/middleware-shallow-link/index.test.ts @@ -1,23 +1,16 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import webdriver from 'next-webdriver' import { join } from 'path' import { check } from 'next-test-utils' describe('browser-shallow-navigation', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), - 'middleware.js': new FileRef(join(__dirname, 'app/middleware.js')), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'app/pages')), + 'middleware.js': new FileRef(join(__dirname, 'app/middleware.js')), + }, }) - afterAll(() => next.destroy()) - it('should render the correct page', async () => { const browser = await webdriver(next.url, '/') diff --git a/test/e2e/middleware-static-files/index.test.ts b/test/e2e/middleware-static-files/index.test.ts index 75461e69e00d..b1abf654eb88 100644 --- a/test/e2e/middleware-static-files/index.test.ts +++ b/test/e2e/middleware-static-files/index.test.ts @@ -1,12 +1,13 @@ /* eslint-env jest */ import { join } from 'path' -import { createNext, FileRef } from 'e2e-utils' -import { isNextStart, NextInstance } from 'e2e-utils' +import { FileRef, isNextStart, nextTestSetup } from 'e2e-utils' import { listClientChunks } from 'next-test-utils' describe('Middleware Runtime', () => { - let next: NextInstance + const { next } = nextTestSetup({ + files: new FileRef(join(__dirname, 'app')), + }) let testPaths: Array<{ testPath: string }> = [ { testPath: '/file.svg' }, { testPath: '/vercel copy.svg' }, @@ -29,15 +30,6 @@ describe('Middleware Runtime', () => { { testPath: '/pages-glob/hello' }, ] - beforeAll(async () => { - next = await createNext({ - files: new FileRef(join(__dirname, 'app')), - }) - }) - afterAll(async () => { - await next.destroy() - }) - it.each(testPaths)( 'should match middleware correctly for $testPath', async ({ testPath }) => { diff --git a/test/e2e/middleware-trailing-slash/test/index.test.ts b/test/e2e/middleware-trailing-slash/test/index.test.ts index 5b535f3acb4d..a780a3c16f75 100644 --- a/test/e2e/middleware-trailing-slash/test/index.test.ts +++ b/test/e2e/middleware-trailing-slash/test/index.test.ts @@ -3,24 +3,16 @@ import fs from 'fs-extra' import { join } from 'path' import webdriver from 'next-webdriver' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { check, fetchViaHTTP, waitFor } from 'next-test-utils' describe('Middleware Runtime trailing slash', () => { - let next: NextInstance - - afterAll(async () => { - await next.destroy() - }) - beforeAll(async () => { - next = await createNext({ - files: { - 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), - 'middleware.js': new FileRef(join(__dirname, '../app/middleware.js')), - pages: new FileRef(join(__dirname, '../app/pages')), - }, - }) + const { next } = nextTestSetup({ + files: { + 'next.config.js': new FileRef(join(__dirname, '../app/next.config.js')), + 'middleware.js': new FileRef(join(__dirname, '../app/middleware.js')), + pages: new FileRef(join(__dirname, '../app/pages')), + }, }) function runTests() { diff --git a/test/e2e/new-link-behavior/child-a-tag-error.test.ts b/test/e2e/new-link-behavior/child-a-tag-error.test.ts index 6c95172e248c..301db17526f8 100644 --- a/test/e2e/new-link-behavior/child-a-tag-error.test.ts +++ b/test/e2e/new-link-behavior/child-a-tag-error.test.ts @@ -1,25 +1,19 @@ -import { createNext, FileRef, isNextDev } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, isNextDev, nextTestSetup } from 'e2e-utils' import webdriver from 'next-webdriver' import path from 'path' const appDir = path.join(__dirname, 'child-a-tag-error') describe('New Link Behavior with child', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(path.join(appDir, 'pages')), - 'next.config.js': new FileRef(path.join(appDir, 'next.config.js')), - }, - dependencies: { - next: 'latest', - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(path.join(appDir, 'pages')), + 'next.config.js': new FileRef(path.join(appDir, 'next.config.js')), + }, + dependencies: { + next: 'latest', + }, }) - afterAll(() => next.destroy()) it('should throw error with child', async () => { const browser = await webdriver(next.url, `/`) diff --git a/test/e2e/new-link-behavior/index.test.ts b/test/e2e/new-link-behavior/index.test.ts index aecc0dcc8215..162519cf47af 100644 --- a/test/e2e/new-link-behavior/index.test.ts +++ b/test/e2e/new-link-behavior/index.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' import webdriver from 'next-webdriver' import cheerio from 'cheerio' @@ -21,18 +20,13 @@ async function matchLogs(browser, includes: string) { const appDir = path.join(__dirname, 'app') describe('New Link Behavior', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(path.join(appDir, 'pages')), - 'next.config.js': new FileRef(path.join(appDir, 'next.config.js')), - }, - dependencies: {}, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(path.join(appDir, 'pages')), + 'next.config.js': new FileRef(path.join(appDir, 'next.config.js')), + }, + dependencies: {}, }) - afterAll(() => next.destroy()) it('should render link with ', async () => { const html = await renderViaHTTP(next.url, '/') diff --git a/test/e2e/new-link-behavior/stitches.test.ts b/test/e2e/new-link-behavior/stitches.test.ts index afad753d4835..8c7422e37bba 100644 --- a/test/e2e/new-link-behavior/stitches.test.ts +++ b/test/e2e/new-link-behavior/stitches.test.ts @@ -1,30 +1,24 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import webdriver from 'next-webdriver' import path from 'path' const appDir = path.join(__dirname, 'stitches') describe('New Link Behavior with stitches', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(path.join(appDir, 'pages')), - components: new FileRef(path.join(appDir, 'components')), - 'next.config.js': new FileRef(path.join(appDir, 'next.config.js')), - 'stitches.config.js': new FileRef( - path.join(appDir, 'stitches.config.js') - ), - }, - dependencies: { - '@stitches/react': '^1.2.6', - next: 'latest', - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(path.join(appDir, 'pages')), + components: new FileRef(path.join(appDir, 'components')), + 'next.config.js': new FileRef(path.join(appDir, 'next.config.js')), + 'stitches.config.js': new FileRef( + path.join(appDir, 'stitches.config.js') + ), + }, + dependencies: { + '@stitches/react': '^1.2.6', + next: 'latest', + }, }) - afterAll(() => next.destroy()) it('should render ', async () => { const browser = await webdriver(next.url, `/`) diff --git a/test/e2e/next-font/basepath.test.ts b/test/e2e/next-font/basepath.test.ts index 464cede64659..f9aea9165fb8 100644 --- a/test/e2e/next-font/basepath.test.ts +++ b/test/e2e/next-font/basepath.test.ts @@ -1,6 +1,5 @@ import cheerio from 'cheerio' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' import { join } from 'path' @@ -9,27 +8,20 @@ const mockedGoogleFontResponses = require.resolve( ) describe('next/font/google basepath', () => { - let next: NextInstance - if ((global as any).isNextDeploy) { it('should skip next deploy for now', () => {}) return } - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'basepath/pages')), - 'next.config.js': new FileRef( - join(__dirname, 'basepath/next.config.js') - ), - }, - env: { - NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'basepath/pages')), + 'next.config.js': new FileRef(join(__dirname, 'basepath/next.config.js')), + }, + env: { + NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, + }, }) - afterAll(() => next.destroy()) test('preload correct files', async () => { const html = await renderViaHTTP(next.url, '/dashboard') diff --git a/test/e2e/next-font/google-fetch-error.test.ts b/test/e2e/next-font/google-fetch-error.test.ts index c0597b48506a..0f277de752b5 100644 --- a/test/e2e/next-font/google-fetch-error.test.ts +++ b/test/e2e/next-font/google-fetch-error.test.ts @@ -1,5 +1,4 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { join } from 'path' import webdriver from 'next-webdriver' @@ -9,25 +8,21 @@ const mockedGoogleFontResponses = require.resolve( describe('next/font/google fetch error', () => { const isDev = (global as any).isNextDev - let next: NextInstance if ((global as any).isNextDeploy) { it('should skip next deploy for now', () => {}) return } - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'google-fetch-error/pages')), - }, - env: { - NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, - }, - skipStart: true, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'google-fetch-error/pages')), + }, + env: { + NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, + }, + skipStart: true, }) - afterAll(() => next.destroy()) if (isDev) { it('should use a fallback font in dev', async () => { diff --git a/test/e2e/next-font/index.test.ts b/test/e2e/next-font/index.test.ts index ace6955799f0..563ad594b758 100644 --- a/test/e2e/next-font/index.test.ts +++ b/test/e2e/next-font/index.test.ts @@ -1,6 +1,5 @@ import cheerio from 'cheerio' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' import { join } from 'path' import webdriver from 'next-webdriver' @@ -38,29 +37,24 @@ function hrefMatchesFontWithoutSizeAdjust(href: string) { } describe('next/font', () => { - let next: NextInstance - if ((global as any).isNextDeploy) { it('should skip next deploy for now', () => {}) return } - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, `app/pages`)), - components: new FileRef(join(__dirname, `app/components`)), - fonts: new FileRef(join(__dirname, `app/fonts`)), - }, - dependencies: { - '@next/font': 'canary', - }, - env: { - NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, `app/pages`)), + components: new FileRef(join(__dirname, `app/components`)), + fonts: new FileRef(join(__dirname, `app/fonts`)), + }, + dependencies: { + '@next/font': 'canary', + }, + env: { + NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, + }, }) - afterAll(() => next.destroy()) if ((global as any).isNextDev) { it('should use production cache control for fonts', async () => { diff --git a/test/e2e/next-font/with-font-declarations-file.test.ts b/test/e2e/next-font/with-font-declarations-file.test.ts index 3dbd9449e546..33adcdbf0781 100644 --- a/test/e2e/next-font/with-font-declarations-file.test.ts +++ b/test/e2e/next-font/with-font-declarations-file.test.ts @@ -1,6 +1,5 @@ import cheerio from 'cheerio' -import { createNext } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' import { join } from 'path' @@ -11,22 +10,17 @@ const mockedGoogleFontResponses = require.resolve( const isDev = (global as any).isNextDev describe('next/font/google with-font-declarations-file', () => { - let next: NextInstance - if ((global as any).isNextDeploy) { it('should skip next deploy for now', () => {}) return } - beforeAll(async () => { - next = await createNext({ - files: join(__dirname, 'with-font-declarations-file'), - env: { - NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, - }, - }) + const { next } = nextTestSetup({ + files: join(__dirname, 'with-font-declarations-file'), + env: { + NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, + }, }) - afterAll(() => next.destroy()) test('preload correct files at /inter', async () => { const html = await renderViaHTTP(next.url, '/inter') diff --git a/test/e2e/next-font/without-preloaded-fonts.test.ts b/test/e2e/next-font/without-preloaded-fonts.test.ts index aa63a8242762..172c5d1b6ba5 100644 --- a/test/e2e/next-font/without-preloaded-fonts.test.ts +++ b/test/e2e/next-font/without-preloaded-fonts.test.ts @@ -1,6 +1,5 @@ import cheerio from 'cheerio' -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' import { join } from 'path' @@ -9,29 +8,24 @@ const mockedGoogleFontResponses = require.resolve( ) describe('next/font/google without-preloaded-fonts without _app', () => { - let next: NextInstance - if ((global as any).isNextDeploy) { it('should skip next deploy for now', () => {}) return } - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/no-preload.js': new FileRef( - join(__dirname, 'without-preloaded-fonts/pages/no-preload.js') - ), - 'pages/without-fonts.js': new FileRef( - join(__dirname, 'without-preloaded-fonts/pages/without-fonts.js') - ), - }, - env: { - NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, - }, - }) + const { next } = nextTestSetup({ + files: { + 'pages/no-preload.js': new FileRef( + join(__dirname, 'without-preloaded-fonts/pages/no-preload.js') + ), + 'pages/without-fonts.js': new FileRef( + join(__dirname, 'without-preloaded-fonts/pages/without-fonts.js') + ), + }, + env: { + NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, + }, }) - afterAll(() => next.destroy()) test('without preload', async () => { const html = await renderViaHTTP(next.url, '/no-preload') @@ -60,32 +54,27 @@ describe('next/font/google without-preloaded-fonts without _app', () => { }) describe('next/font/google no preloads with _app', () => { - let next: NextInstance - if ((global as any).isNextDeploy) { it('should skip next deploy for now', () => {}) return } - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/_app.js': new FileRef( - join(__dirname, 'without-preloaded-fonts/pages/_app.js') - ), - 'pages/no-preload.js': new FileRef( - join(__dirname, 'without-preloaded-fonts/pages/no-preload.js') - ), - 'pages/without-fonts.js': new FileRef( - join(__dirname, 'without-preloaded-fonts/pages/without-fonts.js') - ), - }, - env: { - NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, - }, - }) + const { next } = nextTestSetup({ + files: { + 'pages/_app.js': new FileRef( + join(__dirname, 'without-preloaded-fonts/pages/_app.js') + ), + 'pages/no-preload.js': new FileRef( + join(__dirname, 'without-preloaded-fonts/pages/no-preload.js') + ), + 'pages/without-fonts.js': new FileRef( + join(__dirname, 'without-preloaded-fonts/pages/without-fonts.js') + ), + }, + env: { + NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, + }, }) - afterAll(() => next.destroy()) test('without preload', async () => { const html = await renderViaHTTP(next.url, '/no-preload') diff --git a/test/e2e/next-form/default/shared-tests.util.ts b/test/e2e/next-form/default/shared-tests.util.ts index 54706bd7f1fd..f5d0a1183e56 100644 --- a/test/e2e/next-form/default/shared-tests.util.ts +++ b/test/e2e/next-form/default/shared-tests.util.ts @@ -1,8 +1,6 @@ -import { nextTestSetup } from 'e2e-utils' +import { isReact18, nextTestSetup } from 'e2e-utils' import { Playwright } from 'next-webdriver' -const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 - // These tests are defined here and used in `app-dir.test.ts` and // `pages-dir.test.ts` so that both test suites can be run in parallel. export function runSharedTests(type: 'app' | 'pages') { diff --git a/test/e2e/next-head/index.test.ts b/test/e2e/next-head/index.test.ts index 5d8d0b082e90..59fcf9a0801e 100644 --- a/test/e2e/next-head/index.test.ts +++ b/test/e2e/next-head/index.test.ts @@ -1,22 +1,16 @@ -import { createNext, FileRef } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { renderViaHTTP } from 'next-test-utils' import cheerio from 'cheerio' import webdriver from 'next-webdriver' -import { NextInstance } from 'e2e-utils' import { join } from 'path' describe('next/head', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, 'app/pages')), - components: new FileRef(join(__dirname, 'app/components')), - }, - }) + const { next } = nextTestSetup({ + files: { + pages: new FileRef(join(__dirname, 'app/pages')), + components: new FileRef(join(__dirname, 'app/components')), + }, }) - afterAll(() => next.destroy()) it(`should place charset element at the top of `, async () => { const browser = await webdriver(next.url, '/') diff --git a/test/e2e/next-image-forward-ref/index.test.ts b/test/e2e/next-image-forward-ref/index.test.ts index aca3824fd413..b57285a65f47 100644 --- a/test/e2e/next-image-forward-ref/index.test.ts +++ b/test/e2e/next-image-forward-ref/index.test.ts @@ -1,23 +1,17 @@ -import { createNext, FileRef } from 'e2e-utils' -import { NextInstance } from 'e2e-utils' +import { FileRef, nextTestSetup } from 'e2e-utils' import { waitFor } from 'next-test-utils' import path from 'path' import webdriver from 'next-webdriver' describe('next-image-forward-ref', () => { - let next: NextInstance - const appDir = path.join(__dirname, 'app') - beforeAll(async () => { - next = await createNext({ - files: new FileRef(appDir), - dependencies: { - 'framer-motion': '7.6.9', - }, - }) + const { next } = nextTestSetup({ + files: new FileRef(appDir), + dependencies: { + 'framer-motion': '7.6.9', + }, }) - afterAll(() => next.destroy()) it('allows framer-motion to animate opacity', async () => { const browser = await webdriver(next.url, '/framer-motion') diff --git a/test/e2e/next-image-new/default/default.test.ts b/test/e2e/next-image-new/default/default.test.ts index 4d1d759f12b4..c1f13965a56d 100644 --- a/test/e2e/next-image-new/default/default.test.ts +++ b/test/e2e/next-image-new/default/default.test.ts @@ -8,12 +8,10 @@ import { listClientChunks, getDeploymentId, } from 'next-test-utils' -import { nextTestSetup, isNextDev } from 'e2e-utils' +import { isReact18, nextTestSetup, isNextDev } from 'e2e-utils' import { existsSync } from 'fs' import { join } from 'path' -const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 - describe('Image Component Default Tests', () => { const { next, skipped } = nextTestSetup({ files: __dirname, diff --git a/test/e2e/next-script/index.test.ts b/test/e2e/next-script/index.test.ts index a68bf5056495..a3c73d6cd3a7 100644 --- a/test/e2e/next-script/index.test.ts +++ b/test/e2e/next-script/index.test.ts @@ -1,48 +1,43 @@ import webdriver, { Playwright } from 'next-webdriver' -import { createNext } from 'e2e-utils' +import { createNext, nextTestSetup } from 'e2e-utils' import { NextInstance } from 'e2e-utils' import { check } from 'next-test-utils' describe('beforeInteractive in document Head', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/_document.js': ` - import { Html, Head, Main, NextScript } from 'next/document' - import Script from 'next/script' + const { next } = nextTestSetup({ + files: { + 'pages/_document.js': ` + import { Html, Head, Main, NextScript } from 'next/document' + import Script from 'next/script' - export default function Document() { - return ( - - - - - -
- - - - ) - } - `, - 'pages/index.js': ` - export default function Home() { - return ( - <> -

Home page

- - ) - } - `, - }, - }) + export default function Document() { + return ( + + + + + +
+ + + + ) + } + `, + 'pages/index.js': ` + export default function Home() { + return ( + <> +

Home page

+ + ) + } + `, + }, }) - afterAll(() => next.destroy()) it('Script is injected server-side', async () => { let browser: Playwright @@ -61,44 +56,39 @@ describe('beforeInteractive in document Head', () => { }) describe('beforeInteractive in document body', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/_document.js': ` - import { Html, Head, Main, NextScript } from 'next/document' - import Script from 'next/script' + const { next } = nextTestSetup({ + files: { + 'pages/_document.js': ` + import { Html, Head, Main, NextScript } from 'next/document' + import Script from 'next/script' - export default function Document() { - return ( - - - -
- - - - -
- - - - ) - } - `, - 'pages/index.js': ` - export default function Home() { - return ( - <> -

Home page

- - ) - } - `, - }, - }) + export default function Document() { + return ( + + + + + +
+ + + + ) + } + `, + 'pages/index.js': ` + export default function Home() { + return ( + <> +

Home page

+ + ) + } + `, + }, }) - afterAll(() => next.destroy()) it('Script is injected server-side', async () => { let browser: Playwright @@ -174,43 +159,38 @@ describe('empty strategy in document Head', () => { }) describe('empty strategy in document body', () => { - let next: NextInstance - - beforeAll(async () => { - next = await createNext({ - files: { - 'pages/_document.js': ` - import { Html, Head, Main, NextScript } from 'next/document' - import Script from 'next/script' + const { next } = nextTestSetup({ + files: { + 'pages/_document.js': ` + import { Html, Head, Main, NextScript } from 'next/document' + import Script from 'next/script' - export default function Document() { - return ( - - - -
- -