From fa01e3ac9463dfbff48244db290fd0508ba6fbbc Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Thu, 17 Aug 2023 10:38:55 +0200 Subject: [PATCH] Revert "fix(47299): allow testing pages with metadata in jsdom test environment (#53578)" This reverts commit 77acd164d33b52bb4a46184f2d0c3d1dcb76f164. --- packages/next/package.json | 1 + .../next/src/build/swc/jest-transformer.ts | 19 +++++++- .../next/src/compiled/jest-docblock/LICENSE | 21 +++++++++ .../next/src/compiled/jest-docblock/index.js | 1 + .../src/compiled/jest-docblock/package.json | 1 + packages/next/taskfile.js | 10 ++++ packages/next/types/misc.d.ts | 5 ++ pnpm-lock.yaml | 10 ++++ test/production/jest/server-only.test.ts | 47 ++++--------------- 9 files changed, 74 insertions(+), 41 deletions(-) create mode 100644 packages/next/src/compiled/jest-docblock/LICENSE create mode 100644 packages/next/src/compiled/jest-docblock/index.js create mode 100644 packages/next/src/compiled/jest-docblock/package.json diff --git a/packages/next/package.json b/packages/next/package.json index 007ffd37b3b6..76d96266a5e5 100644 --- a/packages/next/package.json +++ b/packages/next/package.json @@ -242,6 +242,7 @@ "image-size": "1.0.0", "is-docker": "2.0.0", "is-wsl": "2.2.0", + "jest-docblock": "29.4.3", "jest-worker": "27.0.0-next.5", "json5": "2.2.3", "jsonwebtoken": "9.0.0", diff --git a/packages/next/src/build/swc/jest-transformer.ts b/packages/next/src/build/swc/jest-transformer.ts index 350a8e2536db..d26911394886 100644 --- a/packages/next/src/build/swc/jest-transformer.ts +++ b/packages/next/src/build/swc/jest-transformer.ts @@ -29,6 +29,7 @@ DEALINGS IN THE SOFTWARE. import vm from 'vm' import { transformSync } from './index' import { getJestSWCOptions } from './options' +import * as docblock from 'next/dist/compiled/jest-docblock' import type { TransformerCreator, TransformOptions, @@ -76,16 +77,30 @@ function isEsm( ) } +function getTestEnvironment( + src: string, + jestConfig: Config.ProjectConfig +): string { + const docblockPragmas = docblock.parse(docblock.extract(src)) + const pragma = docblockPragmas['jest-environment'] + const environment = + (Array.isArray(pragma) ? pragma[0] : pragma) ?? jestConfig.testEnvironment + return environment +} + const createTransformer: TransformerCreator< SyncTransformer, JestTransformerConfig > = (inputOptions) => ({ process(src, filename, jestOptions) { const jestConfig = getJestConfig(jestOptions) + const testEnvironment = getTestEnvironment(src, jestConfig) const swcTransformOpts = getJestSWCOptions({ - // Always target server when compiling during test, to pass server-only validations and allow testing pages with metadatas - isServer: true, + // When target is node it's similar to the server option set in SWC. + isServer: + testEnvironment === 'node' || + testEnvironment.includes('jest-environment-node'), filename, jsConfig: inputOptions?.jsConfig, resolvedBaseUrl: inputOptions?.resolvedBaseUrl, diff --git a/packages/next/src/compiled/jest-docblock/LICENSE b/packages/next/src/compiled/jest-docblock/LICENSE new file mode 100644 index 000000000000..b93be90515cc --- /dev/null +++ b/packages/next/src/compiled/jest-docblock/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Meta Platforms, Inc. and affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/next/src/compiled/jest-docblock/index.js b/packages/next/src/compiled/jest-docblock/index.js new file mode 100644 index 000000000000..240ca16b9aac --- /dev/null +++ b/packages/next/src/compiled/jest-docblock/index.js @@ -0,0 +1 @@ +(()=>{"use strict";var e={381:e=>{const detectNewline=e=>{if(typeof e!=="string"){throw new TypeError("Expected a string")}const t=e.match(/(?:\r?\n)/g)||[];if(t.length===0){return}const r=t.filter((e=>e==="\r\n")).length;const n=t.length-r;return r>n?"\r\n":"\n"};e.exports=detectNewline;e.exports.graceful=e=>typeof e==="string"&&detectNewline(e)||"\n"},37:e=>{e.exports=require("os")}};var t={};function __nccwpck_require__(r){var n=t[r];if(n!==undefined){return n.exports}var s=t[r]={exports:{}};var c=true;try{e[r](s,s.exports,__nccwpck_require__);c=false}finally{if(c)delete t[r]}return s.exports}if(typeof __nccwpck_require__!=="undefined")__nccwpck_require__.ab=__dirname+"/";var r={};(()=>{var e=r;Object.defineProperty(e,"__esModule",{value:true});e.extract=extract;e.parse=parse;e.parseWithComments=parseWithComments;e.print=print;e.strip=strip;function _os(){const e=__nccwpck_require__(37);_os=function(){return e};return e}function _detectNewline(){const e=_interopRequireDefault(__nccwpck_require__(381));_detectNewline=function(){return e};return e}function _interopRequireDefault(e){return e&&e.__esModule?e:{default:e}}const t=/\*\/$/;const n=/^\/\*\*?/;const s=/^\s*(\/\*\*?(.|\r?\n)*?\*\/)/;const c=/(^|\s+)\/\/([^\r\n]*)/g;const o=/^(\r?\n)+/;const i=/(?:^|\r?\n) *(@[^\r\n]*?) *\r?\n *(?![^@\r\n]*\/\/[^]*)([^@\r\n\s][^@\r\n]+?) *\r?\n/g;const a=/(?:^|\r?\n) *@(\S+) *([^\r\n]*)/g;const u=/(\r?\n|^) *\* ?/g;const p=[];function extract(e){const t=e.match(s);return t?t[0].trimLeft():""}function strip(e){const t=e.match(s);return t&&t[0]?e.substring(t[0].length):e}function parse(e){return parseWithComments(e).pragmas}function parseWithComments(e){const r=(0,_detectNewline().default)(e)??_os().EOL;e=e.replace(n,"").replace(t,"").replace(u,"$1");let s="";while(s!==e){s=e;e=e.replace(i,`${r}$1 $2${r}`)}e=e.replace(o,"").trimRight();const _=Object.create(null);const l=e.replace(a,"").replace(o,"").trimRight();let f;while(f=a.exec(e)){const e=f[2].replace(c,"");if(typeof _[f[1]]==="string"||Array.isArray(_[f[1]])){_[f[1]]=p.concat(_[f[1]],e)}else{_[f[1]]=e}}return{comments:l,pragmas:_}}function print({comments:e="",pragmas:t={}}){const r=(0,_detectNewline().default)(e)??_os().EOL;const n="/**";const s=" *";const c=" */";const o=Object.keys(t);const i=o.map((e=>printKeyValues(e,t[e]))).reduce(((e,t)=>e.concat(t)),[]).map((e=>`${s} ${e}${r}`)).join("");if(!e){if(o.length===0){return""}if(o.length===1&&!Array.isArray(t[o[0]])){const e=t[o[0]];return`${n} ${printKeyValues(o[0],e)[0]}${c}`}}const a=e.split(r).map((e=>`${s} ${e}`)).join(r)+r;return n+r+(e?a:"")+(e&&o.length?s+r:"")+i+c}function printKeyValues(e,t){return p.concat(t).map((t=>`@${e} ${t}`.trim()))}})();module.exports=r})(); \ No newline at end of file diff --git a/packages/next/src/compiled/jest-docblock/package.json b/packages/next/src/compiled/jest-docblock/package.json new file mode 100644 index 000000000000..78bc406c0d04 --- /dev/null +++ b/packages/next/src/compiled/jest-docblock/package.json @@ -0,0 +1 @@ +{"name":"jest-docblock","main":"index.js","license":"MIT"} diff --git a/packages/next/taskfile.js b/packages/next/taskfile.js index f38289c3d498..495da5ef0946 100644 --- a/packages/next/taskfile.js +++ b/packages/next/taskfile.js @@ -2195,6 +2195,15 @@ export async function ncc_https_proxy_agent(task, opts) { .target('src/compiled/https-proxy-agent') } +// eslint-disable-next-line camelcase +externals['jest-docblock'] = 'next/dist/compiled/jest-docblock' +export async function ncc_jest_docblock(task, opts) { + await task + .source(relative(__dirname, require.resolve('jest-docblock'))) + .ncc({ packageName: 'jest-docblock', externals }) + .target('src/compiled/jest-docblock') +} + export async function precompile(task, opts) { await task.parallel( [ @@ -2329,6 +2338,7 @@ export async function ncc(task, opts) { 'ncc_opentelemetry_api', 'ncc_http_proxy_agent', 'ncc_https_proxy_agent', + 'ncc_jest_docblock', 'ncc_mini_css_extract_plugin', ], opts diff --git a/packages/next/types/misc.d.ts b/packages/next/types/misc.d.ts index 6767c2ee691a..e0f9735b2bca 100644 --- a/packages/next/types/misc.d.ts +++ b/packages/next/types/misc.d.ts @@ -458,3 +458,8 @@ declare module 'next/dist/compiled/@opentelemetry/api' { import * as m from '@opentelemetry/api' export = m } + +declare module 'next/dist/compiled/jest-docblock' { + import m from 'jest-docblock' + export = m +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10f1f0c1fb4c..07d83d350eff 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1117,6 +1117,9 @@ importers: is-wsl: specifier: 2.2.0 version: 2.2.0 + jest-docblock: + specifier: 29.4.3 + version: 29.4.3 jest-worker: specifier: 27.0.0-next.5 version: 27.0.0-next.5 @@ -16267,6 +16270,13 @@ packages: detect-newline: 3.1.0 dev: true + /jest-docblock@29.4.3: + resolution: {integrity: sha512-fzdTftThczeSD9nZ3fzA/4KkHtnmllawWrXO69vtI+L9WjEIuXWs4AmyME7lN5hU7dB0sHhuPfcKofRsUb/2Fg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + dependencies: + detect-newline: 3.1.0 + dev: true + /jest-each@27.5.1: resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} diff --git a/test/production/jest/server-only.test.ts b/test/production/jest/server-only.test.ts index 904ba30198dc..8cff3b170cff 100644 --- a/test/production/jest/server-only.test.ts +++ b/test/production/jest/server-only.test.ts @@ -8,6 +8,10 @@ describe('next/jest', () => { next = await createNext({ skipStart: true, files: { + 'app/page.jsx': `import { PI } from '../lib/util' + export default function Home() { + return

{PI}

+ }`, 'app/layout.jsx': `export default function RootLayout({ children }) { return ( @@ -15,12 +19,6 @@ describe('next/jest', () => { ) }`, - - 'app/page.jsx': `import { PI } from '../lib/util' - export default function Home() { - return

{PI}

- }`, - 'app/page.test.jsx': `import { render, screen } from '@testing-library/react' import '@testing-library/jest-dom' import Page from './page' @@ -29,42 +27,13 @@ describe('next/jest', () => { render() expect(screen.getByRole('heading')).toHaveTextContent('3.14') })`, - - 'app/[blog]/page.jsx': `import { Metadata } from 'next' - - export async function generateMetadata({ - params: { blog: title }, - }) { - return { title, description: 'A blog post about ' + title } - } - - export default function Page({ params }) { - return

All about {params.blog}

- } - `, - - 'app/[blog]/page.test.jsx': `import { render, screen } from '@testing-library/react' - import '@testing-library/jest-dom' - import Page from './page' - - describe('Blog Page', () => { - it('has the appropriate title', () => { - render() - expect(screen.getByRole('heading')).toHaveTextContent('All about Jane') - }) - }) - `, - - 'lib/util.js': ` + 'lib/util.js': `/** @jest-environment node */ import 'server-only' export const PI = 3.14;`, - - 'lib/utils.test.ts': ` - import { PI } from './util' + 'lib/utils.test.ts': `import { PI } from './util' it('works from server-side code', () => { expect(PI).toEqual(3.14) })`, - 'jest.config.js': `module.exports = require('next/jest')({ dir: './' })({ testEnvironment: 'jsdom' })`, }, buildCommand: `yarn jest`, @@ -79,11 +48,11 @@ describe('next/jest', () => { afterAll(() => next.destroy()) - it('can run test against server server only code', async () => { + it('can run test against server side components', async () => { try { await next.start() } finally { - expect(next.cliOutput).toInclude('Tests: 3 passed, 3 total') + expect(next.cliOutput).toInclude('Tests: 2 passed, 2 total') } }) })