Skip to content

Commit

Permalink
Add pagesDir to Jest transformer (#36599)
Browse files Browse the repository at this point in the history
Fixes #35469

Adds `pagesDir` which is required for the Relay transform.

Added a test case based on https://github.com/hanford/relay-swc-jest.



## Bug

- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Errors have helpful link attached, see `contributing.md`

## Feature

- [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR.
- [ ] Related issues linked using `fixes #number`
- [ ] Integration tests added
- [ ] Documentation added
- [ ] Telemetry added. In case of a feature if it's used or not.
- [ ] Errors have helpful link attached, see `contributing.md`

## Documentation / Examples

- [ ] Make sure the linting passes by running `yarn lint`


Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com>
  • Loading branch information
timneutkens and ijjk committed May 1, 2022
1 parent bd3dfe1 commit 5aa54b3
Show file tree
Hide file tree
Showing 14 changed files with 344 additions and 1 deletion.
5 changes: 5 additions & 0 deletions packages/next/build/jest/jest.ts
Expand Up @@ -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)
Expand Down Expand Up @@ -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'

Expand Down Expand Up @@ -108,6 +112,7 @@ export default function nextJest(options: { dir?: string } = {}) {
jsConfig,
resolvedBaseUrl,
isEsmProject,
pagesDir,
},
],
// Allow for appending/overriding the default transforms
Expand Down
1 change: 1 addition & 0 deletions packages/next/build/swc/jest-transformer.js
Expand Up @@ -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),
Expand Down
2 changes: 2 additions & 0 deletions packages/next/build/swc/options.js
Expand Up @@ -145,6 +145,7 @@ export function getJestSWCOptions({
esm,
nextConfig,
jsConfig,
pagesDir,
// This is not passed yet as "paths" resolving needs a test first
// resolvedBaseUrl,
}) {
Expand Down Expand Up @@ -174,6 +175,7 @@ export function getJestSWCOptions({
},
disableNextSsg: true,
disablePageConfig: true,
pagesDir,
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/lib/e2e-utils.ts
Expand Up @@ -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, '..')

Expand Down
Expand Up @@ -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: {
Expand Down
23 changes: 23 additions & 0 deletions 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()),
})
20 changes: 20 additions & 0 deletions 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', '<rootDir>/'],
testEnvironment: 'jest-environment-jsdom',
moduleNameMapper: {
// When changing these, also look at the tsconfig!
'^types/(.+)$': '<rootDir>/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)
19 changes: 19 additions & 0 deletions 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!
}
9 changes: 9 additions & 0 deletions test/production/jest/relay/app/next.config.js
@@ -0,0 +1,9 @@
module.exports = {
compiler: {
relay: {
src: './',
artifactDirectory: './types',
language: 'typescript',
},
},
}
10 changes: 10 additions & 0 deletions 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 (
<RelayEnvironmentProvider environment={RelayEnvironment}>
<Component {...pageProps} />
</RelayEnvironmentProvider>
)
}
35 changes: 35 additions & 0 deletions test/production/jest/relay/app/pages/index.tsx
@@ -0,0 +1,35 @@
import { graphql, useRelayEnvironment, QueryRenderer } from 'react-relay'

import type { pagesQueryResponse } from '../types/pagesQuery.graphql'

function Component() {
const env = useRelayEnvironment()
return (
<QueryRenderer
environment={env}
query={graphql`
query pagesQuery {
viewer {
user {
id
name
}
}
}
`}
render={({ props }: { props: pagesQueryResponse }) => {
if (props) {
return (
<div>
Data requested: <span>{props.viewer.user.id}</span>
</div>
)
}

return <div>Loading...</div>
}}
/>
)
}

export default Component
23 changes: 23 additions & 0 deletions 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"]
}
107 changes: 107 additions & 0 deletions test/production/jest/relay/app/types/pagesQuery.graphql.ts
@@ -0,0 +1,107 @@
/**
* @generated SignedSource<<a6468d297da1928100bce80db9f1b8ab>>
* @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

0 comments on commit 5aa54b3

Please sign in to comment.