feat: correctly subs out ability to boot REST API within same process
This commit is contained in:
@@ -13,3 +13,31 @@ module.exports = {
|
||||
},
|
||||
verbose: true,
|
||||
}
|
||||
|
||||
// // NextJS way of doing it
|
||||
// const path = require('path')
|
||||
|
||||
// const nextJest = require('next/jest')
|
||||
|
||||
// // Optionally provide path to Next.js app which will enable loading next.config.js and .env files
|
||||
// const createJestConfig = nextJest({ dir: path.resolve(__dirname, './test/REST_API') })
|
||||
|
||||
// // Any custom config you want to pass to Jest
|
||||
// const customJestConfig = {
|
||||
// globalSetup: './test/jest.setup.ts',
|
||||
// moduleNameMapper: {
|
||||
// '\\.(css|scss)$': '<rootDir>/packages/payload/src/bundlers/mocks/emptyModule.js',
|
||||
// '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
|
||||
// '<rootDir>/packages/payload/src/bundlers/mocks/fileMock.js',
|
||||
// },
|
||||
// testEnvironment: 'node',
|
||||
// testMatch: ['<rootDir>/packages/payload/src/**/*.spec.ts', '<rootDir>/test/**/*int.spec.ts'],
|
||||
// testTimeout: 90000,
|
||||
// transform: {
|
||||
// '^.+\\.(t|j)sx?$': ['@swc/jest'],
|
||||
// },
|
||||
// verbose: true,
|
||||
// }
|
||||
|
||||
// // createJestConfig is exported in this way to ensure that next/jest can load the Next.js config which is async
|
||||
// module.exports = createJestConfig(customJestConfig)
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
"lint-staged": "^14.0.1",
|
||||
"minimist": "1.2.8",
|
||||
"mongodb-memory-server": "8.13.0",
|
||||
"next": "14.0.2",
|
||||
"next": "14.1.1-canary.26",
|
||||
"node-fetch": "2.6.12",
|
||||
"nodemon": "3.0.2",
|
||||
"prettier": "^3.0.3",
|
||||
@@ -102,7 +102,6 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "18.2.0",
|
||||
"react-i18next": "11.18.6",
|
||||
"react-router-dom": "5.3.4"
|
||||
},
|
||||
"engines": {
|
||||
|
||||
@@ -24,7 +24,7 @@ export const connect: Connect = async function connect(this: MongooseAdapter, pa
|
||||
useFacet: undefined,
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
if ([process.env.APP_ENV, process.env.NODE_ENV].includes('test')) {
|
||||
if (process.env.PAYLOAD_TEST_MONGO_URL) {
|
||||
urlToConnect = process.env.PAYLOAD_TEST_MONGO_URL
|
||||
} else {
|
||||
|
||||
@@ -51,8 +51,7 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"http-status": "1.6.2",
|
||||
"i18next": "22.5.1",
|
||||
"next": "^14.0.0",
|
||||
"next": "14.1.1-canary.26",
|
||||
"payload": "^2.0.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
5047
pnpm-lock.yaml
generated
5047
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
packages:
|
||||
# all packages in direct subdirs of packages/
|
||||
- 'packages/*'
|
||||
- 'test/REST_API'
|
||||
# exclude packages that are inside test directories
|
||||
- '!**/test/**'
|
||||
# - '!**/test/**'
|
||||
|
||||
3
test/REST_API/.env.test
Normal file
3
test/REST_API/.env.test
Normal file
@@ -0,0 +1,3 @@
|
||||
PAYLOAD_SECRET=PAYLOAD_CUSTOM_SERVER_EXAMPLE_SECRET_KEY
|
||||
APP_ENV=test
|
||||
__NEXT_TEST_MODE=jest
|
||||
5
test/REST_API/next-env.d.ts
vendored
Normal file
5
test/REST_API/next-env.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
37
test/REST_API/next.config.js
Normal file
37
test/REST_API/next.config.js
Normal file
@@ -0,0 +1,37 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
experimental: {
|
||||
outputFileTracingExcludes: {
|
||||
'**/*': ['drizzle-kit', 'drizzle-kit/utils'],
|
||||
},
|
||||
serverComponentsExternalPackages: [
|
||||
'drizzle-kit',
|
||||
'drizzle-kit/utils',
|
||||
'pino',
|
||||
'pino-pretty',
|
||||
'mongodb-memory-server',
|
||||
],
|
||||
},
|
||||
reactStrictMode: false,
|
||||
// transpilePackages: ['@payloadcms/db-mongodb', 'mongoose'],
|
||||
webpack: (config) => {
|
||||
if (process.env.PAYLOAD_CONFIG_PATH) {
|
||||
config.resolve.alias['payload-config'] = process.env.PAYLOAD_CONFIG_PATH
|
||||
}
|
||||
|
||||
return {
|
||||
...config,
|
||||
externals: [
|
||||
...config.externals,
|
||||
'drizzle-kit',
|
||||
'drizzle-kit/utils',
|
||||
'pino',
|
||||
'pino-pretty',
|
||||
'mongoose',
|
||||
'sharp',
|
||||
],
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
26
test/REST_API/package.json
Normal file
26
test/REST_API/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "dev",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"start": "next start",
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@payloadcms/db-mongodb": "workspace:*",
|
||||
"@payloadcms/next": "workspace:*",
|
||||
"next": "14.1.1-canary.26",
|
||||
"payload": "workspace:*",
|
||||
"react": "18.3.0-canary-247738465-20240130",
|
||||
"react-dom": "18.3.0-canary-247738465-20240130"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"sass": "^1.69.5",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
4
test/REST_API/src/app/(payload)/api/[...slug]/route.ts
Normal file
4
test/REST_API/src/app/(payload)/api/[...slug]/route.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
/* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
|
||||
/* DO NOT MODIFY it because it could be re-written at any time. */
|
||||
|
||||
export { DELETE, GET, PATCH, POST } from '@payloadcms/next/routes'
|
||||
3
test/REST_API/src/app/(payload)/api/test/route.ts
Normal file
3
test/REST_API/src/app/(payload)/api/test/route.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const GET = async (request: Request) => {
|
||||
return Response.json({ message: 'Hello world!' })
|
||||
}
|
||||
32
test/REST_API/tsconfig.json
Normal file
32
test/REST_API/tsconfig.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": false,
|
||||
"noEmit": true,
|
||||
"incremental": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"target": "ESNext",
|
||||
"composite": true, // Make sure typescript knows that this module depends on their references
|
||||
"allowImportingTsExtensions": true,
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@payloadcms/db-mongodb": ["../../packages/db-mongodb/src"],
|
||||
"@payloadcms/next/*": ["../../packages/next/src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"],
|
||||
"composite": true, // Make sure typescript knows that this module depends on their references
|
||||
"references": [{ "path": "../../packages/next" }, { "path": "../../packages/db-mongodb" }]
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import jwtDecode from 'jwt-decode'
|
||||
|
||||
import type { Payload } from '../../packages/payload/src'
|
||||
import type { User } from '../../packages/payload/src/auth'
|
||||
|
||||
import payload from '../../packages/payload/src'
|
||||
import configPromise from '../collections-graphql/config'
|
||||
import { devUser } from '../credentials'
|
||||
import { initPayloadTest } from '../helpers/configHelpers'
|
||||
@@ -13,6 +13,7 @@ require('isomorphic-fetch')
|
||||
|
||||
let apiUrl
|
||||
let client: GraphQLClient
|
||||
let payload: Payload
|
||||
|
||||
const headers = {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -22,7 +23,11 @@ const { email, password } = devUser
|
||||
|
||||
describe('Auth', () => {
|
||||
beforeAll(async () => {
|
||||
const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } })
|
||||
const { serverURL, payload: payloadClient } = await initPayloadTest({
|
||||
__dirname,
|
||||
init: { local: false },
|
||||
})
|
||||
payload = payloadClient
|
||||
apiUrl = `${serverURL}/api`
|
||||
const config = await configPromise
|
||||
const url = `${serverURL}${config.routes.api}${config.routes.graphQL}`
|
||||
@@ -653,6 +658,14 @@ describe('Auth', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('REST API', () => {
|
||||
it('should respond from route handlers', async () => {
|
||||
const test = await fetch(`${apiUrl}/api/test`)
|
||||
|
||||
expect(test.status).toStrictEqual(200)
|
||||
})
|
||||
})
|
||||
|
||||
describe('API Key', () => {
|
||||
it('should authenticate via the correct API key user', async () => {
|
||||
const usersQuery = await payload.find({
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import swcRegister from '@swc/register'
|
||||
import express from 'express'
|
||||
import getPort from 'get-port'
|
||||
import { createServer } from 'http'
|
||||
import next from 'next'
|
||||
import path from 'path'
|
||||
import shelljs from 'shelljs'
|
||||
import { parse } from 'url'
|
||||
|
||||
import type { Payload } from '../../packages/payload/src'
|
||||
import type { InitOptions } from '../../packages/payload/src/config/types'
|
||||
|
||||
import payload from '../../packages/payload/src'
|
||||
import { getPayload } from '../../packages/payload/src'
|
||||
|
||||
type Options = {
|
||||
__dirname: string
|
||||
@@ -40,10 +42,6 @@ export async function initPayloadTest(options: Options): Promise<InitializedPayl
|
||||
process.env.PAYLOAD_DROP_DATABASE = 'true'
|
||||
process.env.NODE_ENV = 'test'
|
||||
|
||||
if (!initOptions?.local) {
|
||||
initOptions.express = express()
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore - bad @swc/register types
|
||||
swcRegister({
|
||||
@@ -59,12 +57,40 @@ export async function initPayloadTest(options: Options): Promise<InitializedPayl
|
||||
},
|
||||
})
|
||||
|
||||
await payload.init(initOptions)
|
||||
const payload = await getPayload(initOptions)
|
||||
|
||||
const port = await getPort()
|
||||
if (initOptions.express) {
|
||||
initOptions.express.listen(port)
|
||||
const serverURL = `http://localhost:${port}`
|
||||
|
||||
if (!initOptions?.local) {
|
||||
// when using middleware `hostname` and `port` must be provided below
|
||||
const app = next({
|
||||
dev: true,
|
||||
hostname: 'localhost',
|
||||
port,
|
||||
dir: path.resolve(__dirname, '../REST_API'),
|
||||
})
|
||||
const handle = app.getRequestHandler()
|
||||
await app.prepare()
|
||||
createServer(async (req, res) => {
|
||||
try {
|
||||
const parsedUrl = parse(req.url, true)
|
||||
|
||||
await handle(req, res, parsedUrl)
|
||||
} catch (err) {
|
||||
console.error('Error occurred handling', req.url, err)
|
||||
res.statusCode = 500
|
||||
res.end('internal server error')
|
||||
}
|
||||
})
|
||||
.once('error', (err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
.listen(port, () => {
|
||||
console.log(`> Ready on ${serverURL}`)
|
||||
})
|
||||
}
|
||||
|
||||
return { serverURL: `http://localhost:${port}`, payload }
|
||||
return { serverURL, payload }
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import payload from '../../packages/payload/src'
|
||||
import { AuthenticationError } from '../../packages/payload/src/errors'
|
||||
import { devUser, regularUser } from '../credentials'
|
||||
import { initPayloadTest } from '../helpers/configHelpers'
|
||||
import { RESTClient } from '../helpers/rest'
|
||||
import { afterOperationSlug } from './collections/AfterOperation'
|
||||
import { chainingHooksSlug } from './collections/ChainingHooks'
|
||||
import { contextHooksSlug } from './collections/ContextHooks'
|
||||
@@ -17,17 +16,14 @@ import {
|
||||
import { relationsSlug } from './collections/Relations'
|
||||
import { transformSlug } from './collections/Transform'
|
||||
import { hooksUsersSlug } from './collections/Users'
|
||||
import configPromise, { HooksConfig } from './config'
|
||||
import { HooksConfig } from './config'
|
||||
import { dataHooksGlobalSlug } from './globals/Data'
|
||||
|
||||
let client: RESTClient
|
||||
let apiUrl
|
||||
|
||||
describe('Hooks', () => {
|
||||
beforeAll(async () => {
|
||||
const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } })
|
||||
const config = await configPromise
|
||||
client = new RESTClient(config, { serverURL, defaultSlug: transformSlug })
|
||||
apiUrl = `${serverURL}/api`
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user