From d5c7b2674507ffd2098638df41a7fd1535753f34 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Sun, 1 May 2022 12:33:20 +0200 Subject: [PATCH 1/6] Add pagesDir to Jest transformer Fixes #35469 Adds `pagesDir` which is required for the Relay transform. I'll add a test case for it based on https://github.com/hanford/relay-swc-jest in a follow-up commit. Co-Authored-By: Wonu Lee --- packages/next/build/jest/jest.ts | 5 +++++ packages/next/build/swc/jest-transformer.js | 1 + packages/next/build/swc/options.js | 2 ++ 3 files changed, 8 insertions(+) diff --git a/packages/next/build/jest/jest.ts b/packages/next/build/jest/jest.ts index 194a0cd81d4f..242fee709f2e 100644 --- a/packages/next/build/jest/jest.ts +++ b/packages/next/build/jest/jest.ts @@ -4,6 +4,7 @@ import loadConfig from '../../server/config' import { PHASE_TEST } from '../../shared/lib/constants' import loadJsConfig from '../load-jsconfig' import * as Log from '../output/log' +import { findPagesDir } from '../../lib/find-pages-dir' async function getConfig(dir: string) { const conf = await loadConfig(PHASE_TEST, dir) @@ -50,8 +51,11 @@ export default function nextJest(options: { dir?: string } = {}) { let jsConfig let resolvedBaseUrl let isEsmProject = false + let pagesDir + if (options.dir) { const resolvedDir = resolve(options.dir) + pagesDir = findPagesDir(resolvedDir) const packageConfig = loadClosestPackageJson(resolvedDir) isEsmProject = packageConfig.type === 'module' @@ -108,6 +112,7 @@ export default function nextJest(options: { dir?: string } = {}) { jsConfig, resolvedBaseUrl, isEsmProject, + pagesDir, }, ], // Allow for appending/overriding the default transforms diff --git a/packages/next/build/swc/jest-transformer.js b/packages/next/build/swc/jest-transformer.js index 18c15b6f2a94..cc11890430ef 100644 --- a/packages/next/build/swc/jest-transformer.js +++ b/packages/next/build/swc/jest-transformer.js @@ -47,6 +47,7 @@ module.exports = { nextConfig: inputOptions.nextConfig, jsConfig: inputOptions.jsConfig, resolvedBaseUrl: inputOptions.resolvedBaseUrl, + pagesDir: inputOptions.pagesDir, esm: isSupportEsm && isEsm(Boolean(inputOptions.isEsmProject), filename, jestConfig), diff --git a/packages/next/build/swc/options.js b/packages/next/build/swc/options.js index 1347012f3aa4..eea0abad7026 100644 --- a/packages/next/build/swc/options.js +++ b/packages/next/build/swc/options.js @@ -145,6 +145,7 @@ export function getJestSWCOptions({ esm, nextConfig, jsConfig, + pagesDir, // This is not passed yet as "paths" resolving needs a test first // resolvedBaseUrl, }) { @@ -174,6 +175,7 @@ export function getJestSWCOptions({ }, disableNextSsg: true, disablePageConfig: true, + pagesDir, } } From 43766c0386f973888ce6935e7e35b35310aa7e7c Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Sun, 1 May 2022 14:45:05 +0200 Subject: [PATCH 2/6] Add test for Relay in Jest --- test/production/{next => }/jest/index.test.ts | 0 .../jest/relay/app/components/environment.tsx | 23 ++++ test/production/jest/relay/app/jest.config.js | 20 ++++ test/production/jest/relay/app/main.graphql | 19 ++++ test/production/jest/relay/app/next.config.js | 9 ++ test/production/jest/relay/app/pages/_app.tsx | 10 ++ .../production/jest/relay/app/pages/index.tsx | 41 +++++++ .../jest/relay/app/tests/entry.test.tsx | 25 ++++ test/production/jest/relay/app/tsconfig.json | 23 ++++ .../relay/app/types/pagesQuery.graphql.ts | 107 ++++++++++++++++++ test/production/jest/relay/relay-jest.test.ts | 50 ++++++++ 11 files changed, 327 insertions(+) rename test/production/{next => }/jest/index.test.ts (100%) create mode 100644 test/production/jest/relay/app/components/environment.tsx create mode 100644 test/production/jest/relay/app/jest.config.js create mode 100644 test/production/jest/relay/app/main.graphql create mode 100644 test/production/jest/relay/app/next.config.js create mode 100644 test/production/jest/relay/app/pages/_app.tsx create mode 100644 test/production/jest/relay/app/pages/index.tsx create mode 100644 test/production/jest/relay/app/tests/entry.test.tsx create mode 100644 test/production/jest/relay/app/tsconfig.json create mode 100644 test/production/jest/relay/app/types/pagesQuery.graphql.ts create mode 100644 test/production/jest/relay/relay-jest.test.ts diff --git a/test/production/next/jest/index.test.ts b/test/production/jest/index.test.ts similarity index 100% rename from test/production/next/jest/index.test.ts rename to test/production/jest/index.test.ts diff --git a/test/production/jest/relay/app/components/environment.tsx b/test/production/jest/relay/app/components/environment.tsx new file mode 100644 index 000000000000..5faba8196b0b --- /dev/null +++ b/test/production/jest/relay/app/components/environment.tsx @@ -0,0 +1,23 @@ +import { Environment, Network, RecordSource, Store } from 'relay-runtime' + +async function fetchGraphQL(text, variables) { + return new Promise((next) => { + const res = () => { + return next({ data: { viewer: { user: { id: '123', name: 'Foo' } } } }) + } + + setTimeout(res, 1000) + }) +} + +// Relay passes a "params" object with the query name and text. So we define a helper function +// to call our fetchGraphQL utility with params.text. +async function fetchRelay(params, variables) { + return await fetchGraphQL(params.text, variables) +} + +// Export a singleton instance of Relay Environment configured with our network function: +export default new Environment({ + network: Network.create(fetchRelay), + store: new Store(new RecordSource()), +}) diff --git a/test/production/jest/relay/app/jest.config.js b/test/production/jest/relay/app/jest.config.js new file mode 100644 index 000000000000..23f86dd58d7c --- /dev/null +++ b/test/production/jest/relay/app/jest.config.js @@ -0,0 +1,20 @@ +const nextJest = require('next/jest') + +const createJestConfig = nextJest({ + // Provide the path to your Next.js app to load next.config.js and .env files in your test environment + dir: './', +}) + +// Add any custom config to be passed to Jest +const customJestConfig = { + // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work + moduleDirectories: ['node_modules', '/'], + testEnvironment: 'jest-environment-jsdom', + moduleNameMapper: { + // When changing these, also look at the tsconfig! + '^types/(.+)$': '/types/$1', + }, +} + +// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async +module.exports = createJestConfig(customJestConfig) diff --git a/test/production/jest/relay/app/main.graphql b/test/production/jest/relay/app/main.graphql new file mode 100644 index 000000000000..84b80aca3dd4 --- /dev/null +++ b/test/production/jest/relay/app/main.graphql @@ -0,0 +1,19 @@ +type Query { + viewer(after: String, before: String, first: Int, last: Int): Viewer! + node(id: ID!): Node +} + +type Viewer implements Node { + id: ID! + user: User! +} + +interface Node { + id: ID! +} + +type User implements Node { + id: ID! + name: String! + title: String! +} diff --git a/test/production/jest/relay/app/next.config.js b/test/production/jest/relay/app/next.config.js new file mode 100644 index 000000000000..27c3e0359afd --- /dev/null +++ b/test/production/jest/relay/app/next.config.js @@ -0,0 +1,9 @@ +module.exports = { + compiler: { + relay: { + src: './', + artifactDirectory: './types', + language: 'typescript', + }, + }, +} diff --git a/test/production/jest/relay/app/pages/_app.tsx b/test/production/jest/relay/app/pages/_app.tsx new file mode 100644 index 000000000000..ec6e4caf2db1 --- /dev/null +++ b/test/production/jest/relay/app/pages/_app.tsx @@ -0,0 +1,10 @@ +import { RelayEnvironmentProvider } from 'react-relay/hooks' +import RelayEnvironment from '../components/environment' + +export default function MyApp({ Component, pageProps }) { + return ( + + + + ) +} diff --git a/test/production/jest/relay/app/pages/index.tsx b/test/production/jest/relay/app/pages/index.tsx new file mode 100644 index 000000000000..fbd5cdd53375 --- /dev/null +++ b/test/production/jest/relay/app/pages/index.tsx @@ -0,0 +1,41 @@ +import { Suspense } from 'react' +import { + useLazyLoadQuery, + graphql, + useRelayEnvironment, + QueryRenderer, +} from 'react-relay' + +import type { pagesQueryResponse } from '../types/pagesQuery.graphql' + +function Component() { + const env = useRelayEnvironment() + return ( + { + if (props) { + return ( +
+ Data requested: {props.viewer.user.id} +
+ ) + } + + return
Loading...
+ }} + /> + ) +} + +export default Component diff --git a/test/production/jest/relay/app/tests/entry.test.tsx b/test/production/jest/relay/app/tests/entry.test.tsx new file mode 100644 index 000000000000..0d6ab6fd34b2 --- /dev/null +++ b/test/production/jest/relay/app/tests/entry.test.tsx @@ -0,0 +1,25 @@ +import { render as renderFn, waitFor } from '@testing-library/react' +import { RelayEnvironmentProvider } from 'react-relay' +import { createMockEnvironment, MockPayloadGenerator } from 'relay-test-utils' + +import Page from '../pages' + +describe('test graphql tag transformation', () => { + it('should work', async () => { + let environment = createMockEnvironment() + + const { getByText } = renderFn( + + + + ) + + environment.mock.resolveMostRecentOperation((operation) => { + return MockPayloadGenerator.generate(operation) + }) + + await waitFor(() => getByText('Data requested:')) + + expect(getByText('Data requested:')).not.toBe(null) + }) +}) diff --git a/test/production/jest/relay/app/tsconfig.json b/test/production/jest/relay/app/tsconfig.json new file mode 100644 index 000000000000..d5a9c1e3401c --- /dev/null +++ b/test/production/jest/relay/app/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "incremental": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "paths": { + "types/*": ["./types/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", "tests/entry.test.tsx"], + "exclude": ["node_modules"] +} diff --git a/test/production/jest/relay/app/types/pagesQuery.graphql.ts b/test/production/jest/relay/app/types/pagesQuery.graphql.ts new file mode 100644 index 000000000000..ea144a81ff5c --- /dev/null +++ b/test/production/jest/relay/app/types/pagesQuery.graphql.ts @@ -0,0 +1,107 @@ +/** + * @generated SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ConcreteRequest, Query } from 'relay-runtime' +export type pagesQuery$variables = {} +export type pagesQueryVariables = pagesQuery$variables +export type pagesQuery$data = { + readonly viewer: { + readonly user: { + readonly id: string + readonly name: string + } + } +} +export type pagesQueryResponse = pagesQuery$data +export type pagesQuery = { + variables: pagesQueryVariables + response: pagesQuery$data +} + +const node: ConcreteRequest = (function () { + var v0 = { + alias: null, + args: null, + kind: 'ScalarField', + name: 'id', + storageKey: null, + }, + v1 = { + alias: null, + args: null, + concreteType: 'User', + kind: 'LinkedField', + name: 'user', + plural: false, + selections: [ + v0 /*: any*/, + { + alias: null, + args: null, + kind: 'ScalarField', + name: 'name', + storageKey: null, + }, + ], + storageKey: null, + } + return { + fragment: { + argumentDefinitions: [], + kind: 'Fragment', + metadata: null, + name: 'pagesQuery', + selections: [ + { + alias: null, + args: null, + concreteType: 'Viewer', + kind: 'LinkedField', + name: 'viewer', + plural: false, + selections: [v1 /*: any*/], + storageKey: null, + }, + ], + type: 'Query', + abstractKey: null, + }, + kind: 'Request', + operation: { + argumentDefinitions: [], + kind: 'Operation', + name: 'pagesQuery', + selections: [ + { + alias: null, + args: null, + concreteType: 'Viewer', + kind: 'LinkedField', + name: 'viewer', + plural: false, + selections: [v1 /*: any*/, v0 /*: any*/], + storageKey: null, + }, + ], + }, + params: { + cacheID: '5a14ce729d0deb2c3170bcdcba33a61a', + id: null, + metadata: {}, + name: 'pagesQuery', + operationKind: 'query', + text: 'query pagesQuery {\n viewer {\n user {\n id\n name\n }\n id\n }\n}\n', + }, + } +})() + +;(node as any).hash = '00b43dedd685e716dda36f66f4d5e30e' + +export default node diff --git a/test/production/jest/relay/relay-jest.test.ts b/test/production/jest/relay/relay-jest.test.ts new file mode 100644 index 000000000000..14ec6ab425b7 --- /dev/null +++ b/test/production/jest/relay/relay-jest.test.ts @@ -0,0 +1,50 @@ +import { createNext, FileRef } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import path from 'path' + +const appDir = path.join(__dirname, 'app') + +describe('next/jest', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + components: new FileRef(path.join(appDir, 'components')), + pages: new FileRef(path.join(appDir, 'pages')), + tests: new FileRef(path.join(appDir, 'tests')), + types: new FileRef(path.join(appDir, 'types')), + 'jest.config.js': new FileRef(path.join(appDir, 'jest.config.js')), + 'next.config.js': new FileRef(path.join(appDir, 'next.config.js')), + 'tsconfig.json': new FileRef(path.join(appDir, 'tsconfig.json')), + 'main.graphql': new FileRef(path.join(appDir, 'main.graphql')), + }, + dependencies: { + jest: '27.4.7', + 'react-relay': '^13.2.0', + '@testing-library/react': '^13.1.1', + '@types/jest': '^27.4.1', + 'babel-jest': '^27.5.1', + 'babel-plugin-relay': '^13.2.0', + jsdom: '^19.0.0', + 'relay-compiler': '^13.0.1', + 'relay-test-utils': '^13.0.2', + typescript: '^4.6.3', + }, + packageJson: { + scripts: { + // Runs jest and bails if jest fails + build: + 'yarn jest --forceExit tests/entry.test.tsx && yarn next build', + }, + }, + buildCommand: `yarn build`, + }) + }) + afterAll(() => next.destroy()) + + it('should work', async () => { + // Suite fails if `jest` fails during `build` + expect(typeof '').toBe('string') + }) +}) From 45e822d5fd6f23299b78f8761e06efe4c15c8849 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Sun, 1 May 2022 15:09:35 +0200 Subject: [PATCH 3/6] Don't run test --- .../jest/relay/app/tests/entry.test.tsx | 25 ---------------- test/production/jest/relay/relay-jest.test.ts | 29 ++++++++++++++++++- 2 files changed, 28 insertions(+), 26 deletions(-) delete mode 100644 test/production/jest/relay/app/tests/entry.test.tsx diff --git a/test/production/jest/relay/app/tests/entry.test.tsx b/test/production/jest/relay/app/tests/entry.test.tsx deleted file mode 100644 index 0d6ab6fd34b2..000000000000 --- a/test/production/jest/relay/app/tests/entry.test.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { render as renderFn, waitFor } from '@testing-library/react' -import { RelayEnvironmentProvider } from 'react-relay' -import { createMockEnvironment, MockPayloadGenerator } from 'relay-test-utils' - -import Page from '../pages' - -describe('test graphql tag transformation', () => { - it('should work', async () => { - let environment = createMockEnvironment() - - const { getByText } = renderFn( - - - - ) - - environment.mock.resolveMostRecentOperation((operation) => { - return MockPayloadGenerator.generate(operation) - }) - - await waitFor(() => getByText('Data requested:')) - - expect(getByText('Data requested:')).not.toBe(null) - }) -}) diff --git a/test/production/jest/relay/relay-jest.test.ts b/test/production/jest/relay/relay-jest.test.ts index 14ec6ab425b7..a798493725b7 100644 --- a/test/production/jest/relay/relay-jest.test.ts +++ b/test/production/jest/relay/relay-jest.test.ts @@ -12,7 +12,34 @@ describe('next/jest', () => { files: { components: new FileRef(path.join(appDir, 'components')), pages: new FileRef(path.join(appDir, 'pages')), - tests: new FileRef(path.join(appDir, 'tests')), + 'tests/entry.test.tsx': ` + import { render as renderFn, waitFor } from '@testing-library/react' + import { RelayEnvironmentProvider } from 'react-relay' + import { createMockEnvironment, MockPayloadGenerator } from 'relay-test-utils' + + import Page from '../pages' + + describe('test graphql tag transformation', () => { + it('should work', async () => { + let environment = createMockEnvironment() + + const { getByText } = renderFn( + + + + ) + + environment.mock.resolveMostRecentOperation((operation) => { + return MockPayloadGenerator.generate(operation) + }) + + await waitFor(() => getByText('Data requested:')) + + expect(getByText('Data requested:')).not.toBe(null) + }) + }) + + `, types: new FileRef(path.join(appDir, 'types')), 'jest.config.js': new FileRef(path.join(appDir, 'jest.config.js')), 'next.config.js': new FileRef(path.join(appDir, 'next.config.js')), From 889fe068b788918b25894f0d9a44bb2f984ff9a8 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sun, 1 May 2022 10:43:01 -0500 Subject: [PATCH 4/6] fix lint --- test/lib/e2e-utils.ts | 2 +- test/production/jest/relay/app/pages/index.tsx | 8 +------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/test/lib/e2e-utils.ts b/test/lib/e2e-utils.ts index 50dda869f45c..c483b411b476 100644 --- a/test/lib/e2e-utils.ts +++ b/test/lib/e2e-utils.ts @@ -7,7 +7,7 @@ import { NextStartInstance } from './next-modes/next-start' import { NextDeployInstance } from './next-modes/next-deploy' // increase timeout to account for yarn install time -jest.setTimeout((process.platform === 'win32' ? 240 : 180) * 1000) +jest.setTimeout(240 * 1000) const testsFolder = path.join(__dirname, '..') diff --git a/test/production/jest/relay/app/pages/index.tsx b/test/production/jest/relay/app/pages/index.tsx index fbd5cdd53375..f03d8d22378b 100644 --- a/test/production/jest/relay/app/pages/index.tsx +++ b/test/production/jest/relay/app/pages/index.tsx @@ -1,10 +1,4 @@ -import { Suspense } from 'react' -import { - useLazyLoadQuery, - graphql, - useRelayEnvironment, - QueryRenderer, -} from 'react-relay' +import { graphql, useRelayEnvironment, QueryRenderer } from 'react-relay' import type { pagesQueryResponse } from '../types/pagesQuery.graphql' From 4c057337cb0b2b784d1f76afe87c7b447e5a0e57 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sun, 1 May 2022 10:57:18 -0500 Subject: [PATCH 5/6] fix react 17 incompat --- test/production/jest/index.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/production/jest/index.test.ts b/test/production/jest/index.test.ts index f981254067a4..79f055b3a5ec 100644 --- a/test/production/jest/index.test.ts +++ b/test/production/jest/index.test.ts @@ -5,6 +5,12 @@ import { renderViaHTTP } from 'next-test-utils' describe('next/jest', () => { let next: NextInstance + if (process.env.NEXT_TEST_REACT_VERSION === '^17') { + // react testing library is specific to react version + it('should bail on react v17', () => {}) + return + } + beforeAll(async () => { next = await createNext({ files: { From bb8c457924b6cc837ff3781ae758a866ead42485 Mon Sep 17 00:00:00 2001 From: JJ Kasper Date: Sun, 1 May 2022 11:20:25 -0500 Subject: [PATCH 6/6] update other test --- test/production/jest/relay/relay-jest.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/production/jest/relay/relay-jest.test.ts b/test/production/jest/relay/relay-jest.test.ts index a798493725b7..405e27236f62 100644 --- a/test/production/jest/relay/relay-jest.test.ts +++ b/test/production/jest/relay/relay-jest.test.ts @@ -7,6 +7,12 @@ const appDir = path.join(__dirname, 'app') describe('next/jest', () => { let next: NextInstance + if (process.env.NEXT_TEST_REACT_VERSION === '^17') { + // react testing library is specific to react version + it('should bail on react v17', () => {}) + return + } + beforeAll(async () => { next = await createNext({ files: {