Compare commits

..

6 Commits

Author SHA1 Message Date
Elliot DeNolf
18ac47c1fc chore: rebase, fix up 2025-01-15 12:21:04 -05:00
Elliot DeNolf
e6f98d1ba9 chore: add writeDefaultAdapter flag, jest args were interfering 2025-01-15 12:19:28 -05:00
Elliot DeNolf
fb148fff0f chore: some strictNullChecks mitigation 2025-01-15 12:19:27 -05:00
Elliot DeNolf
aa0b6ebf61 chore(tests): uncomment more suites to build 2025-01-15 12:19:27 -05:00
Elliot DeNolf
549b8c8e87 chore(tests): add prebuild to ensure database adapter exists 2025-01-15 12:19:22 -05:00
Elliot DeNolf
f5ea7b47cf build: partial test suite build, runs with build:all 2025-01-15 12:19:12 -05:00
33 changed files with 247 additions and 1390 deletions

View File

@@ -46,7 +46,7 @@
"build:storage-s3": "turbo build --filter \"@payloadcms/storage-s3\"",
"build:storage-uploadthing": "turbo build --filter \"@payloadcms/storage-uploadthing\"",
"build:storage-vercel-blob": "turbo build --filter \"@payloadcms/storage-vercel-blob\"",
"build:tests": "pnpm --filter payload-test-suite run typecheck",
"build:tests": "pnpm --filter payload-test-suite run build",
"build:translations": "turbo build --filter \"@payloadcms/translations\"",
"build:ui": "turbo build --filter \"@payloadcms/ui\"",
"clean": "turbo clean",
@@ -94,7 +94,7 @@
"test:int:postgres": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=postgres DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
"test:int:sqlite": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 PAYLOAD_DATABASE=sqlite DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=test/jest.config.js --runInBand",
"test:types": "tstyche",
"test:unit": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true vitest --run packages",
"test:unit": "cross-env NODE_OPTIONS=\"--no-deprecation\" NODE_NO_WARNINGS=1 DISABLE_LOGGING=true jest --forceExit --detectOpenHandles --config=jest.config.js --runInBand",
"translateNewKeys": "pnpm --filter translations run translateNewKeys"
},
"lint-staged": {
@@ -171,8 +171,7 @@
"tstyche": "^3.1.1",
"tsx": "4.19.2",
"turbo": "^2.3.3",
"typescript": "5.7.2",
"vitest": "^2.1.8"
"typescript": "5.7.2"
},
"peerDependencies": {
"react": "^19.0.0 || ^19.0.0-rc-65a56d0e-20241020",

View File

@@ -1,4 +1,4 @@
import { vi, describe } from 'vitest'
import { jest } from '@jest/globals'
import fs from 'fs'
import fse from 'fs-extra'
import globby from 'globby'
@@ -15,7 +15,7 @@ describe('createProject', () => {
let projectDir: string
beforeAll(() => {
// eslint-disable-next-line no-console
console.log = vi.fn()
console.log = jest.fn()
})
beforeEach(() => {
@@ -29,7 +29,7 @@ describe('createProject', () => {
}
})
describe('#createProject', { timeout: 30_000 }, () => {
describe('#createProject', () => {
const args = {
_: ['project-name'],
'--db': 'mongodb',

View File

@@ -1,5 +1,5 @@
import * as p from '@clack/prompts'
import { vi } from 'vitest'
import { jest } from '@jest/globals'
import { parseAndModifyConfigContent, withPayloadStatement } from './wrap-next-config.js'
@@ -159,7 +159,7 @@ describe('parseAndInsertWithPayload', () => {
// Unsupported: export { wrapped as default }
it('should give warning with a named export as default', async () => {
const warnLogSpy = vi.spyOn(p.log, 'warn').mockImplementation(() => {})
const warnLogSpy = jest.spyOn(p.log, 'warn').mockImplementation(() => {})
const { modifiedConfigContent, success } = await parseAndModifyConfigContent(
esmConfigs.nextConfigExportNamedDefault,

View File

@@ -1,6 +1,6 @@
import type { Transporter } from 'nodemailer'
import { MockedFunction, vi } from 'vitest'
import { jest } from '@jest/globals'
import nodemailer from 'nodemailer'
import type { NodemailerAdapterArgs } from './index.js'
@@ -14,11 +14,11 @@ const defaultArgs: NodemailerAdapterArgs = {
describe('email-nodemailer', () => {
describe('transport verification', () => {
let mockedVerify: MockedFunction<Transporter['verify']>
let mockedVerify: jest.Mock<Transporter['verify']>
let mockTransport: Transporter
beforeEach(() => {
mockedVerify = vi.fn()
mockedVerify = jest.fn<Transporter['verify']>()
mockTransport = nodemailer.createTransport({
name: 'existing-transport',
// eslint-disable-next-line @typescript-eslint/require-await, @typescript-eslint/no-misused-promises

View File

@@ -1,6 +1,6 @@
import type { Payload } from 'payload'
import { vi } from 'vitest'
import { jest } from '@jest/globals'
import { resendAdapter } from './index.js'
@@ -16,19 +16,19 @@ describe('email-resend', () => {
const mockPayload = {} as unknown as Payload
afterEach(() => {
vi.clearAllMocks()
jest.clearAllMocks()
})
it('should handle sending an email', async () => {
global.fetch = vi.spyOn(global, 'fetch').mockImplementation(
vi.fn(() =>
global.fetch = jest.spyOn(global, 'fetch').mockImplementation(
jest.fn(() =>
Promise.resolve({
json: () => {
return { id: 'test-id' }
},
}),
),
)
) as jest.Mock,
) as jest.Mock
const adapter = resendAdapter({
apiKey,
@@ -62,15 +62,13 @@ describe('email-resend', () => {
message: 'error information',
statusCode: 403,
}
// @ts-expect-error
global.fetch = vi.spyOn(global, 'fetch').mockImplementation(
// @ts-expect-error
vi.fn(() =>
global.fetch = jest.spyOn(global, 'fetch').mockImplementation(
jest.fn(() =>
Promise.resolve({
json: () => errorResponse,
}),
),
)
) as jest.Mock,
) as jest.Mock
const adapter = resendAdapter({
apiKey,

View File

@@ -1,6 +1,6 @@
import type { Config, Payload } from 'payload'
import { vi } from 'vitest'
import { jest } from '@jest/globals'
import nodemailer from 'nodemailer'
import { defaults } from 'payload'
@@ -12,11 +12,11 @@ describe('email', () => {
const defaultDomain = 'test.com'
const apiKey = 'test'
const mockedPayload: Payload = vi.fn() as unknown as Payload
const mockedPayload: Payload = jest.fn() as unknown as Payload
beforeAll(() => {
// Mock createTestAccount to prevent calling external services
vi.spyOn(nodemailer, 'createTestAccount').mockImplementation(() => {
jest.spyOn(nodemailer, 'createTestAccount').mockImplementation(() => {
return Promise.resolve({
imap: { host: 'imap.test.com', port: 993, secure: true },
pass: 'testpass',

View File

@@ -1,32 +1,32 @@
import type { Config, Payload } from 'payload'
import { vi, MockedFunction } from 'vitest'
import { jest } from '@jest/globals'
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import nodemailer from 'nodemailer'
import { defaults } from 'payload'
// TO-DO: this would be needed for the TO-DO tests below.
// maybe we have to use vi.unstable_mockModule? (already tried)
// vi.mock('./plugin.ts', () => ({
// // generateRandomString: vi.fn<() => string>().mockReturnValue('instance'),
// generateRandomString: vi.fn().mockReturnValue('instance'),
// maybe we have to use jest.unstable_mockModule? (already tried)
// jest.mock('./plugin.ts', () => ({
// // generateRandomString: jest.fn<() => string>().mockReturnValue('instance'),
// generateRandomString: jest.fn().mockReturnValue('instance'),
// }))
const mockedPayload: Payload = {
updateGlobal: vi.fn(),
findGlobal: vi.fn().mockReturnValue('instance'),
updateGlobal: jest.fn(),
findGlobal: jest.fn().mockReturnValue('instance'),
} as unknown as Payload
import { payloadCloudPlugin } from './plugin.js'
describe('plugin', () => {
let createTransportSpy: MockedFunction<typeof nodemailer.createTransport>
let createTransportSpy: jest.Spied<any>
const skipVerify = true
beforeAll(() => {
// Mock createTestAccount to prevent calling external services
vi.spyOn(nodemailer, 'createTestAccount').mockImplementation(() => {
jest.spyOn(nodemailer, 'createTestAccount').mockImplementation(() => {
return Promise.resolve({
imap: { host: 'imap.test.com', port: 993, secure: true },
pass: 'testpass',
@@ -39,13 +39,12 @@ describe('plugin', () => {
})
beforeEach(() => {
// @ts-expect-error
createTransportSpy = vi.spyOn(nodemailer, 'createTransport').mockImplementationOnce(() => {
createTransportSpy = jest.spyOn(nodemailer, 'createTransport').mockImplementationOnce(() => {
return {
transporter: {
name: 'Nodemailer - SMTP',
},
verify: vi.fn(),
verify: jest.fn(),
} as unknown as ReturnType<typeof nodemailer.createTransport>
})
})
@@ -68,6 +67,7 @@ describe('plugin', () => {
})
describe('storage', () => {
// eslint-disable-next-line jest/expect-expect
it('should default to using payload cloud storage', async () => {
const plugin = payloadCloudPlugin()
const config = await plugin(createConfig())
@@ -75,6 +75,7 @@ describe('plugin', () => {
assertCloudStorage(config)
})
// eslint-disable-next-line jest/expect-expect
it('should allow opt-out', async () => {
const plugin = payloadCloudPlugin({ storage: false })
const config = await plugin(createConfig())
@@ -113,7 +114,7 @@ describe('plugin', () => {
})
it('should not modify existing email transport', async () => {
const logSpy = vi.spyOn(console, 'log')
const logSpy = jest.spyOn(console, 'log')
const existingTransport = nodemailer.createTransport({
name: 'existing-transport',

View File

@@ -1,10 +1,10 @@
import { vi } from 'vitest'
import { jest } from '@jest/globals'
import type { ValidateOptions } from './config/types.js'
import { number, password, point, relationship, select, text, textarea } from './validations.js'
const t = vi.fn((string) => string)
const t = jest.fn((string) => string)
let options: ValidateOptions<any, any, any> = {
data: undefined,

View File

@@ -1,7 +1,6 @@
import type { AfterErrorHook, AfterErrorHookArgs, Config, PayloadRequest } from 'payload'
import { APIError, defaults } from 'payload'
import { vi } from 'vitest'
import { sentryPlugin } from './index'
import { randomUUID } from 'crypto'
@@ -71,7 +70,7 @@ describe('@payloadcms/plugin-sentry - unit', () => {
collection: { slug: 'mock-slug' } as any,
}
const captureExceptionSpy = vi.spyOn(mockSentry, 'captureException')
const captureExceptionSpy = jest.spyOn(mockSentry, 'captureException')
await hook(afterApiErrorHookArgs)

800
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -42,5 +42,23 @@ export default buildConfigWithDefaults({
},
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
schema: [
({ jsonSchema }) => {
if (jsonSchema.definitions) {
jsonSchema.definitions.objectWithNumber = {
type: 'object',
additionalProperties: false,
properties: {
id: {
type: 'number',
required: true,
},
},
required: true,
}
}
return jsonSchema
},
],
},
})

View File

@@ -82,14 +82,16 @@ describe('create-payload-app', () => {
expect(firstResult.success).toEqual(false)
// Move all files from app to top-level directory named `(app)`
if (firstResult.success === false && 'nextAppDir' in firstResult) {
fs.mkdirSync(path.resolve(firstResult.nextAppDir, '(app)'))
fs.readdirSync(path.resolve(firstResult.nextAppDir)).forEach((file) => {
if (file === '(app)') return
fs.renameSync(
path.resolve(firstResult.nextAppDir, file),
path.resolve(firstResult.nextAppDir, '(app)', file),
)
if (
firstResult.success === false &&
'nextAppDir' in firstResult &&
typeof firstResult.nextAppDir === 'string'
) {
const nextAppDir = firstResult.nextAppDir
fs.mkdirSync(path.resolve(nextAppDir, '(app)'))
fs.readdirSync(path.resolve(nextAppDir)).forEach((file) => {
if (file === '(app)') {return}
fs.renameSync(path.resolve(nextAppDir, file), path.resolve(nextAppDir, '(app)', file))
})
}
@@ -124,7 +126,7 @@ describe('create-payload-app', () => {
}
// Check that `@payload-config` path is added to tsconfig
expect(userTsConfig.compilerOptions.paths?.['@payload-config']).toStrictEqual([
expect(userTsConfig.compilerOptions?.paths?.['@payload-config']).toStrictEqual([
`./${result.isSrcDir ? 'src/' : ''}payload.config.ts`,
])

View File

@@ -1,2 +0,0 @@
/media
/media-gif

View File

@@ -1,35 +0,0 @@
import type { CollectionConfig } from 'payload'
import { getPayload } from 'payload'
export const mediaSlug = 'media'
export const MediaCollection: CollectionConfig = {
slug: mediaSlug,
access: {
create: () => true,
read: () => true,
},
fields: [],
upload: {
crop: true,
focalPoint: true,
imageSizes: [
{
name: 'thumbnail',
height: 200,
width: 200,
},
{
name: 'medium',
height: 800,
width: 800,
},
{
name: 'large',
height: 1200,
width: 1200,
},
],
},
}

View File

@@ -1,34 +0,0 @@
import type { CollectionConfig } from 'payload'
import { mediaSlug } from '../Media/index.js'
export const postsSlug = 'posts'
export const PostsCollection: CollectionConfig = {
slug: postsSlug,
admin: {
useAsTitle: 'text',
},
fields: [
{
name: 'text',
type: 'text',
},
{
type: 'row',
fields: [],
},
{
name: 'associatedMedia',
type: 'upload',
access: {
create: () => true,
update: () => false,
},
relationTo: mediaSlug,
},
],
versions: {
drafts: true,
},
}

View File

@@ -1,58 +0,0 @@
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
import path from 'path'
import { getFileByPath } from 'payload'
import { fileURLToPath } from 'url'
import { buildConfigWithDefaults } from '../buildConfigWithDefaults.js'
import { devUser } from '../credentials.js'
import { MediaCollection } from './collections/Media/index.js'
import { PostsCollection, postsSlug } from './collections/Posts/index.js'
import { MenuGlobal } from './globals/Menu/index.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfigWithDefaults({
admin: {
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [PostsCollection, MediaCollection],
email: nodemailerAdapter(),
globals: [MenuGlobal],
onInit: async (payload) => {
await payload.create({
collection: 'users',
data: {
email: devUser.email,
password: devUser.password,
},
})
await payload.create({
collection: postsSlug,
data: {
text: 'example post',
},
})
const email = await payload.sendEmail({
subject: 'This was sent on init',
to: 'test@example.com',
})
// Create image
const imageFilePath = path.resolve(dirname, '../uploads/image.png')
const imageFile = await getFileByPath(imageFilePath)
await payload.create({
collection: 'media',
data: {},
file: imageFile,
})
},
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
})

View File

@@ -1,19 +0,0 @@
import { rootParserOptions } from '../../eslint.config.js'
import testEslintConfig from '../eslint.config.js'
/** @typedef {import('eslint').Linter.Config} Config */
/** @type {Config[]} */
export const index = [
...testEslintConfig,
{
languageOptions: {
parserOptions: {
...rootParserOptions,
tsconfigRootDir: import.meta.dirname,
},
},
},
]
export default index

View File

@@ -1,13 +0,0 @@
import type { GlobalConfig } from 'payload'
export const menuSlug = 'menu'
export const MenuGlobal: GlobalConfig = {
slug: menuSlug,
fields: [
{
name: 'globalText',
type: 'text',
},
],
}

View File

@@ -1,341 +0,0 @@
/* tslint:disable */
/* eslint-disable */
/**
* This file was automatically generated by Payload.
* DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config,
* and re-run `payload generate:types` to regenerate this file.
*/
export interface Config {
auth: {
users: UserAuthOperations;
};
collections: {
posts: Post;
media: Media;
users: User;
'payload-locked-documents': PayloadLockedDocument;
'payload-preferences': PayloadPreference;
'payload-migrations': PayloadMigration;
};
collectionsJoins: {};
collectionsSelect: {
posts: PostsSelect<false> | PostsSelect<true>;
media: MediaSelect<false> | MediaSelect<true>;
users: UsersSelect<false> | UsersSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
};
db: {
defaultIDType: string;
};
globals: {
menu: Menu;
};
globalsSelect: {
menu: MenuSelect<false> | MenuSelect<true>;
};
locale: null;
user: User & {
collection: 'users';
};
jobs: {
tasks: unknown;
workflows: unknown;
};
}
export interface UserAuthOperations {
forgotPassword: {
email: string;
password: string;
};
login: {
email: string;
password: string;
};
registerFirstUser: {
email: string;
password: string;
};
unlock: {
email: string;
password: string;
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "posts".
*/
export interface Post {
id: string;
text?: string | null;
associatedMedia?: (string | null) | Media;
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media".
*/
export interface Media {
id: string;
updatedAt: string;
createdAt: string;
url?: string | null;
thumbnailURL?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
focalX?: number | null;
focalY?: number | null;
sizes?: {
thumbnail?: {
url?: string | null;
width?: number | null;
height?: number | null;
mimeType?: string | null;
filesize?: number | null;
filename?: string | null;
};
medium?: {
url?: string | null;
width?: number | null;
height?: number | null;
mimeType?: string | null;
filesize?: number | null;
filename?: string | null;
};
large?: {
url?: string | null;
width?: number | null;
height?: number | null;
mimeType?: string | null;
filesize?: number | null;
filename?: string | null;
};
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users".
*/
export interface User {
id: string;
updatedAt: string;
createdAt: string;
email: string;
resetPasswordToken?: string | null;
resetPasswordExpiration?: string | null;
salt?: string | null;
hash?: string | null;
loginAttempts?: number | null;
lockUntil?: string | null;
password?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents".
*/
export interface PayloadLockedDocument {
id: string;
document?:
| ({
relationTo: 'posts';
value: string | Post;
} | null)
| ({
relationTo: 'media';
value: string | Media;
} | null)
| ({
relationTo: 'users';
value: string | User;
} | null);
globalSlug?: string | null;
user: {
relationTo: 'users';
value: string | User;
};
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences".
*/
export interface PayloadPreference {
id: string;
user: {
relationTo: 'users';
value: string | User;
};
key?: string | null;
value?:
| {
[k: string]: unknown;
}
| unknown[]
| string
| number
| boolean
| null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations".
*/
export interface PayloadMigration {
id: string;
name?: string | null;
batch?: number | null;
updatedAt: string;
createdAt: string;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "posts_select".
*/
export interface PostsSelect<T extends boolean = true> {
text?: T;
associatedMedia?: T;
updatedAt?: T;
createdAt?: T;
_status?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "media_select".
*/
export interface MediaSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
url?: T;
thumbnailURL?: T;
filename?: T;
mimeType?: T;
filesize?: T;
width?: T;
height?: T;
focalX?: T;
focalY?: T;
sizes?:
| T
| {
thumbnail?:
| T
| {
url?: T;
width?: T;
height?: T;
mimeType?: T;
filesize?: T;
filename?: T;
};
medium?:
| T
| {
url?: T;
width?: T;
height?: T;
mimeType?: T;
filesize?: T;
filename?: T;
};
large?:
| T
| {
url?: T;
width?: T;
height?: T;
mimeType?: T;
filesize?: T;
filename?: T;
};
};
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "users_select".
*/
export interface UsersSelect<T extends boolean = true> {
updatedAt?: T;
createdAt?: T;
email?: T;
resetPasswordToken?: T;
resetPasswordExpiration?: T;
salt?: T;
hash?: T;
loginAttempts?: T;
lockUntil?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-locked-documents_select".
*/
export interface PayloadLockedDocumentsSelect<T extends boolean = true> {
document?: T;
globalSlug?: T;
user?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-preferences_select".
*/
export interface PayloadPreferencesSelect<T extends boolean = true> {
user?: T;
key?: T;
value?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-migrations_select".
*/
export interface PayloadMigrationsSelect<T extends boolean = true> {
name?: T;
batch?: T;
updatedAt?: T;
createdAt?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "menu".
*/
export interface Menu {
id: string;
globalText?: string | null;
updatedAt?: string | null;
createdAt?: string | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "menu_select".
*/
export interface MenuSelect<T extends boolean = true> {
globalText?: T;
updatedAt?: T;
createdAt?: T;
globalType?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "auth".
*/
export interface Auth {
[k: string]: unknown;
}
declare module 'payload' {
// @ts-ignore
export interface GeneratedTypes extends Config {}
}

View File

@@ -1,13 +0,0 @@
{
// extend your base config to share compilerOptions, etc
//"extends": "./tsconfig.json",
"compilerOptions": {
// ensure that nobody can accidentally use this config for a build
"noEmit": true
},
"include": [
// whatever paths you intend to lint
"./**/*.ts",
"./**/*.tsx"
]
}

View File

@@ -1,3 +0,0 @@
{
"extends": "../tsconfig.json"
}

View File

@@ -30,11 +30,13 @@ export const collectionEndpoints: CollectionConfig['endpoints'] = [
try {
data = await req.json()
} catch (error) {
} catch (ignore) {
data = {}
}
if (data) req.data = data
if (data) {
req.data = data
}
return Response.json({
name: req.data.name,

View File

@@ -80,6 +80,7 @@ export const allDatabaseAdapters = {
export function generateDatabaseAdapter(dbAdapter) {
const databaseAdapter = allDatabaseAdapters[dbAdapter]
if (!databaseAdapter) {
console.log({ dbAdapter })
throw new Error(`Unknown database adapter: ${dbAdapter}`)
}
fs.writeFileSync(
@@ -94,3 +95,11 @@ export function generateDatabaseAdapter(dbAdapter) {
console.log('Wrote', dbAdapter, 'db adapter')
return databaseAdapter
}
// Run if called directly. Used in test suite build: pnpm build:tests
if (import.meta.url === `file://${filename}`) {
const writeDefaultAdapter = process.argv.find((arg) => arg === '--writeDefaultAdapter')
if (writeDefaultAdapter) {
generateDatabaseAdapter('mongodb')
}
}

View File

@@ -141,7 +141,7 @@ export class NextRESTClient {
headers: this.buildHeaders(options),
method: 'DELETE',
})
return this._DELETE(request, { params: Promise.resolve({ slug }) })
return this._DELETE(request, { params: new Promise(() => ({ slug })) })
}
async GET(
@@ -157,7 +157,7 @@ export class NextRESTClient {
headers: this.buildHeaders(options),
method: 'GET',
})
return this._GET(request, { params: Promise.resolve({ slug }) })
return this._GET(request, { params: new Promise(() => ({ slug })) })
}
async GRAPHQL_POST(options: RequestInit & RequestOptions): Promise<Response> {
@@ -213,7 +213,7 @@ export class NextRESTClient {
headers: this.buildHeaders(options),
method: 'PATCH',
})
return this._PATCH(request, { params: Promise.resolve({ slug }) })
return this._PATCH(request, { params: new Promise(() => ({ slug })) })
}
async POST(
@@ -227,19 +227,6 @@ export class NextRESTClient {
headers: this.buildHeaders(options),
method: 'POST',
})
return this._POST(request, { params: Promise.resolve({ slug }) })
}
async PUT(path: ValidPath, options: FileArg & RequestInit & RequestOptions): Promise<Response> {
const { slug, params, url } = this.generateRequestParts(path)
const { query, ...rest } = options
const queryParams = generateQueryString(query, params)
const request = new Request(`${url}${queryParams}`, {
...rest,
headers: this.buildHeaders(options),
method: 'PUT',
})
return this._PUT(request, { params: Promise.resolve({ slug }) })
return this._POST(request, { params: new Promise(() => ({ slug })) })
}
}

View File

@@ -4,7 +4,6 @@ import type { PgTable } from 'drizzle-orm/pg-core'
import type { SQLiteTable } from 'drizzle-orm/sqlite-core'
import type { Payload } from 'payload'
import { GenericTable } from '@payloadcms/drizzle/types'
import { sql } from 'drizzle-orm'
import { isMongoose } from './isMongoose.js'
@@ -55,6 +54,7 @@ async function createDrizzleSnapshot(db: PostgresAdapter | SQLiteAdapter, snapsh
for (const tableName in schema) {
const table = db.drizzle.query[tableName]['fullSchema'][tableName] //db.drizzle._.schema[tableName]
// @ts-expect-error - Our type overrides will not work in the test directory
const records = await db.drizzle.select().from(table).execute()
snapshot[tableName] = records
}
@@ -85,18 +85,22 @@ async function restoreFromDrizzleSnapshot(
// Temporarily disable foreign key constraint checks
try {
await db.execute({
// @ts-expect-error - Our type overrides will not work in the test directory
drizzle: db.drizzle,
raw: disableFKConstraintChecksQuery,
})
for (const tableName in dbSnapshot[snapshotKey]) {
const table = db.drizzle.query[tableName]['fullSchema'][tableName]
await db.execute({
// @ts-expect-error - Our type overrides will not work in the test directory
drizzle: db.drizzle,
// @ts-expect-error - Our type overrides will not work in the test directory
sql: sql`DELETE FROM ${table}`,
}) // This deletes all records from the table. Probably not necessary, as I'm deleting the table before restoring anyways
const records = dbSnapshot[snapshotKey][tableName]
if (records.length > 0) {
// @ts-expect-error - Our type overrides will not work in the test directory
await db.drizzle.insert(table).values(records).execute()
}
}
@@ -105,6 +109,7 @@ async function restoreFromDrizzleSnapshot(
} finally {
// Re-enable foreign key constraint checks
await db.execute({
// @ts-expect-error - Our type overrides will not work in the test directory
drizzle: db.drizzle,
raw: enableFKConstraintChecksQuery,
})

View File

@@ -5,11 +5,12 @@
"description": "Payload test suite",
"type": "module",
"scripts": {
"prebuild": "node --no-deprecation --import @swc-node/register/esm-register generateDatabaseAdapter.ts --writeDefaultAdapter",
"build": "tsc --project tsconfig.typecheck.json",
"dev": "cross-env NODE_OPTIONS=--no-deprecation node ./dev.js",
"test": "pnpm -C \"../\" run test",
"test:e2e": "pnpm -C \"../\" run test:e2e",
"test:int": "pnpm -C \"../\" run test:int",
"typecheck": "pnpm turbo build --filter payload-test-suite && tsc --project tsconfig.typecheck.json"
"test:int": "pnpm -C \"../\" run test:int"
},
"lint-staged": {
"**/package.json": "sort-package-json",

View File

@@ -8,7 +8,7 @@ import { devUser } from '../credentials.js'
import { Media } from './collections/Media.js'
import { MediaWithPrefix } from './collections/MediaWithPrefix.js'
import { Users } from './collections/Users.js'
import { mediaSlug, mediaWithPrefixSlug, prefix } from './shared.js'
import { prefix } from './shared.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
@@ -38,8 +38,8 @@ export default buildConfigWithDefaults({
plugins: [
azureStorage({
collections: {
[mediaSlug]: true,
[mediaWithPrefixSlug]: {
media: true,
'media-with-prefix': {
prefix,
},
},

View File

@@ -1,3 +1,5 @@
import type { CollectionSlug } from 'payload'
import { gcsStorage } from '@payloadcms/storage-gcs'
import dotenv from 'dotenv'
import { fileURLToPath } from 'node:url'
@@ -38,8 +40,8 @@ export default buildConfigWithDefaults({
plugins: [
gcsStorage({
collections: {
[mediaSlug]: true,
[mediaWithPrefixSlug]: {
media: true,
'media-with-prefix': {
prefix,
},
},

View File

@@ -8,7 +8,7 @@ import { devUser } from '../credentials.js'
import { Media } from './collections/Media.js'
import { MediaWithPrefix } from './collections/MediaWithPrefix.js'
import { Users } from './collections/Users.js'
import { mediaSlug, mediaWithPrefixSlug, prefix } from './shared.js'
import { prefix } from './shared.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
@@ -38,8 +38,8 @@ export default buildConfigWithDefaults({
plugins: [
vercelBlobStorage({
collections: {
[mediaSlug]: true,
[mediaWithPrefixSlug]: {
media: true,
'media-with-prefix': {
prefix,
},
},

View File

@@ -36,9 +36,9 @@ export interface Config {
user: User & {
collection: 'users';
};
jobs?: {
jobs: {
tasks: unknown;
workflows?: unknown;
workflows: unknown;
};
}
export interface UserAuthOperations {

View File

@@ -1,15 +1,93 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"incremental": false
"allowJs": true,
"baseUrl": ".",
"checkJs": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": ["dom", "dom.iterable", "esnext"],
"module": "NodeNext",
"moduleResolution": "NodeNext",
"noEmit": true,
"resolveJsonModule": true,
"rootDir": ".",
"skipLibCheck": true,
"target": "esnext",
"types": ["jest", "node", "@types/jest"],
// "incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@payload-config": ["./_community/config.ts"]
}
},
"exclude": ["dist", "build", "temp", "node_modules"],
"exclude": ["dist", "build", "temp", "node_modules", "dist/**/*.js", "**/dist/**/*.js"],
"include": [
// "./test/_community/**/*.ts"
// "./_community/**/*.ts"
"/**/*.ts"
// "../packages/**/src/**/*.ts",
// "../packages/**/src/**/*.tsx"
// Uncomment these as they are able to be compiled
"./_community/**/*.ts",
// "./access-control/**/*.ts",
// "./admin/**/*.ts",
// "./admin-root/**/*.ts",
"./app/**/*.ts",
// "./array-update/**/*.ts",
// "./auth/**/*.ts",
// "./collections-graphql/**/*.ts",
// "./collections-rest/**/*.ts",
// "./config/**/*.ts",
"./create-payload-app/**/*.ts",
"./custom-graphql/**/*.ts",
// "./database/**/*.ts",
// "./dataloader/**/*.ts",
"./email/**/*.ts",
"./email-nodemailer/**/*.ts",
"./email-resend/**/*.ts",
"./endpoints/**/*.ts",
// "./field-error-states/**/*.ts",
// "./field-perf/**/*.ts",
// "./fields/**/*.ts",
// "./fields-relationship/**/*.ts",
// "./globals/**/*.ts",
// "./graphql-schema-gen/**/*.ts",
// "./helpers/**/*.ts",
// "./hooks/**/*.ts",
// "./i18n/**/*.ts",
// "./import-test/**/*.ts",
// "./joins/**/*.ts",
// "./live-preview/**/*.ts",
// "./loader/**/*.ts",
// "./localization/**/*.ts",
// "./localization-rtl/**/*.ts",
// "./locked-documents/**/*.ts",
// "./login-with-username/**/*.ts",
"./migrations-cli/**/*.ts",
// "./nested-fields/**/*.ts",
"./payload-cloud/**/*.ts",
// "./plugin-cloud-storage/**/*.ts",
// "./plugin-form-builder/**/*.ts",
// "./plugin-nested-docs/**/*.ts",
// "./plugin-redirects/**/*.ts",
// "./plugin-search/**/*.ts",
"./plugin-sentry/**/*.ts",
// "./plugin-seo/**/*.ts",
// "./plugin-stripe/**/*.ts",
// "./plugins/**/*.ts",
// "./relationships/**/*.ts",
"./scripts/**/*.ts",
// "./sort/**/*.ts",
"./storage-azure/**/*.ts",
"./storage-gcs/**/*.ts",
// "./storage-s3/**/*.ts",
"./storage-uploadthing/**/*.ts",
"./storage-vercel-blob/**/*.ts"
// "./uploads/**/*.ts",
// "./versions/**/*.ts"
],
"references": [
{
@@ -21,6 +99,12 @@
{
"path": "../packages/db-postgres"
},
{
"path": "../packages/db-sqlite"
},
{
"path": "../packages/drizzle"
},
{
"path": "../packages/graphql"
},
@@ -69,6 +153,21 @@
{
"path": "../packages/richtext-lexical"
},
{
"path": "../packages/storage-azure"
},
{
"path": "../packages/storage-gcs"
},
{
"path": "../packages/storage-s3"
},
{
"path": "../packages/storage-uploadthing"
},
{
"path": "../packages/storage-vercel-blob"
},
{
"path": "../packages/translations"
},

View File

@@ -59,6 +59,6 @@
"@payloadcms/next": ["./packages/next/src/exports/*"]
}
},
"include": ["${configDir}/src", "vite.config.ts"],
"include": ["${configDir}/src"],
"exclude": ["${configDir}/dist", "${configDir}/build", "${configDir}/temp", "**/*.spec.ts"]
}

View File

@@ -1,11 +0,0 @@
import { configDefaults, defineConfig } from 'vitest/config'
export default defineConfig({
test: {
environment: 'node',
globals: true,
testTimeout: 20_000,
// setupFiles: ['./src/test/setup.ts'],
exclude: [...configDefaults.exclude, 'src/app/**/*'],
include: ['**/*.spec.ts'],
},
})