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,
|
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",
|
"lint-staged": "^14.0.1",
|
||||||
"minimist": "1.2.8",
|
"minimist": "1.2.8",
|
||||||
"mongodb-memory-server": "8.13.0",
|
"mongodb-memory-server": "8.13.0",
|
||||||
"next": "14.0.2",
|
"next": "14.1.1-canary.26",
|
||||||
"node-fetch": "2.6.12",
|
"node-fetch": "2.6.12",
|
||||||
"nodemon": "3.0.2",
|
"nodemon": "3.0.2",
|
||||||
"prettier": "^3.0.3",
|
"prettier": "^3.0.3",
|
||||||
@@ -102,7 +102,6 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-i18next": "11.18.6",
|
|
||||||
"react-router-dom": "5.3.4"
|
"react-router-dom": "5.3.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const connect: Connect = async function connect(this: MongooseAdapter, pa
|
|||||||
useFacet: undefined,
|
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) {
|
if (process.env.PAYLOAD_TEST_MONGO_URL) {
|
||||||
urlToConnect = process.env.PAYLOAD_TEST_MONGO_URL
|
urlToConnect = process.env.PAYLOAD_TEST_MONGO_URL
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -51,8 +51,7 @@
|
|||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"http-status": "1.6.2",
|
"http-status": "1.6.2",
|
||||||
"i18next": "22.5.1",
|
"next": "14.1.1-canary.26",
|
||||||
"next": "^14.0.0",
|
|
||||||
"payload": "^2.0.0"
|
"payload": "^2.0.0"
|
||||||
},
|
},
|
||||||
"publishConfig": {
|
"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:
|
packages:
|
||||||
# all packages in direct subdirs of packages/
|
# all packages in direct subdirs of packages/
|
||||||
- 'packages/*'
|
- 'packages/*'
|
||||||
|
- 'test/REST_API'
|
||||||
# exclude packages that are inside test directories
|
# 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 { GraphQLClient } from 'graphql-request'
|
||||||
import jwtDecode from 'jwt-decode'
|
import jwtDecode from 'jwt-decode'
|
||||||
|
|
||||||
|
import type { Payload } from '../../packages/payload/src'
|
||||||
import type { User } from '../../packages/payload/src/auth'
|
import type { User } from '../../packages/payload/src/auth'
|
||||||
|
|
||||||
import payload from '../../packages/payload/src'
|
|
||||||
import configPromise from '../collections-graphql/config'
|
import configPromise from '../collections-graphql/config'
|
||||||
import { devUser } from '../credentials'
|
import { devUser } from '../credentials'
|
||||||
import { initPayloadTest } from '../helpers/configHelpers'
|
import { initPayloadTest } from '../helpers/configHelpers'
|
||||||
@@ -13,6 +13,7 @@ require('isomorphic-fetch')
|
|||||||
|
|
||||||
let apiUrl
|
let apiUrl
|
||||||
let client: GraphQLClient
|
let client: GraphQLClient
|
||||||
|
let payload: Payload
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -22,7 +23,11 @@ const { email, password } = devUser
|
|||||||
|
|
||||||
describe('Auth', () => {
|
describe('Auth', () => {
|
||||||
beforeAll(async () => {
|
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`
|
apiUrl = `${serverURL}/api`
|
||||||
const config = await configPromise
|
const config = await configPromise
|
||||||
const url = `${serverURL}${config.routes.api}${config.routes.graphQL}`
|
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', () => {
|
describe('API Key', () => {
|
||||||
it('should authenticate via the correct API key user', async () => {
|
it('should authenticate via the correct API key user', async () => {
|
||||||
const usersQuery = await payload.find({
|
const usersQuery = await payload.find({
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import swcRegister from '@swc/register'
|
import swcRegister from '@swc/register'
|
||||||
import express from 'express'
|
|
||||||
import getPort from 'get-port'
|
import getPort from 'get-port'
|
||||||
|
import { createServer } from 'http'
|
||||||
|
import next from 'next'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import shelljs from 'shelljs'
|
import shelljs from 'shelljs'
|
||||||
|
import { parse } from 'url'
|
||||||
|
|
||||||
import type { Payload } from '../../packages/payload/src'
|
import type { Payload } from '../../packages/payload/src'
|
||||||
import type { InitOptions } from '../../packages/payload/src/config/types'
|
import type { InitOptions } from '../../packages/payload/src/config/types'
|
||||||
|
|
||||||
import payload from '../../packages/payload/src'
|
import { getPayload } from '../../packages/payload/src'
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
__dirname: string
|
__dirname: string
|
||||||
@@ -40,10 +42,6 @@ export async function initPayloadTest(options: Options): Promise<InitializedPayl
|
|||||||
process.env.PAYLOAD_DROP_DATABASE = 'true'
|
process.env.PAYLOAD_DROP_DATABASE = 'true'
|
||||||
process.env.NODE_ENV = 'test'
|
process.env.NODE_ENV = 'test'
|
||||||
|
|
||||||
if (!initOptions?.local) {
|
|
||||||
initOptions.express = express()
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore - bad @swc/register types
|
// @ts-ignore - bad @swc/register types
|
||||||
swcRegister({
|
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()
|
const port = await getPort()
|
||||||
if (initOptions.express) {
|
const serverURL = `http://localhost:${port}`
|
||||||
initOptions.express.listen(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 { AuthenticationError } from '../../packages/payload/src/errors'
|
||||||
import { devUser, regularUser } from '../credentials'
|
import { devUser, regularUser } from '../credentials'
|
||||||
import { initPayloadTest } from '../helpers/configHelpers'
|
import { initPayloadTest } from '../helpers/configHelpers'
|
||||||
import { RESTClient } from '../helpers/rest'
|
|
||||||
import { afterOperationSlug } from './collections/AfterOperation'
|
import { afterOperationSlug } from './collections/AfterOperation'
|
||||||
import { chainingHooksSlug } from './collections/ChainingHooks'
|
import { chainingHooksSlug } from './collections/ChainingHooks'
|
||||||
import { contextHooksSlug } from './collections/ContextHooks'
|
import { contextHooksSlug } from './collections/ContextHooks'
|
||||||
@@ -17,17 +16,14 @@ import {
|
|||||||
import { relationsSlug } from './collections/Relations'
|
import { relationsSlug } from './collections/Relations'
|
||||||
import { transformSlug } from './collections/Transform'
|
import { transformSlug } from './collections/Transform'
|
||||||
import { hooksUsersSlug } from './collections/Users'
|
import { hooksUsersSlug } from './collections/Users'
|
||||||
import configPromise, { HooksConfig } from './config'
|
import { HooksConfig } from './config'
|
||||||
import { dataHooksGlobalSlug } from './globals/Data'
|
import { dataHooksGlobalSlug } from './globals/Data'
|
||||||
|
|
||||||
let client: RESTClient
|
|
||||||
let apiUrl
|
let apiUrl
|
||||||
|
|
||||||
describe('Hooks', () => {
|
describe('Hooks', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } })
|
const { serverURL } = await initPayloadTest({ __dirname, init: { local: false } })
|
||||||
const config = await configPromise
|
|
||||||
client = new RESTClient(config, { serverURL, defaultSlug: transformSlug })
|
|
||||||
apiUrl = `${serverURL}/api`
|
apiUrl = `${serverURL}/api`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user