chore: adjust email pattern
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import type { Transporter } from 'nodemailer'
|
import type { Transporter } from 'nodemailer'
|
||||||
import type SMTPConnection from 'nodemailer/lib/smtp-connection'
|
import type SMTPConnection from 'nodemailer/lib/smtp-connection'
|
||||||
import type { EmailAdapter, SendMailOptions } from 'payload/types'
|
import type { EmailAdapter } from 'payload/config'
|
||||||
|
|
||||||
import nodemailer from 'nodemailer'
|
import nodemailer from 'nodemailer'
|
||||||
import { InvalidConfiguration } from 'payload/errors'
|
import { InvalidConfiguration } from 'payload/errors'
|
||||||
@@ -14,19 +14,19 @@ export type NodemailerAdapterArgs = {
|
|||||||
transportOptions?: SMTPConnection.Options
|
transportOptions?: SMTPConnection.Options
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodemailerAdapter = EmailAdapter<SendMailOptions, unknown>
|
export type NodemailerAdapter = EmailAdapter<unknown>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an email adapter using nodemailer
|
* Creates an email adapter using nodemailer
|
||||||
*
|
*
|
||||||
* If no email configuration is provided, an ethereal email test account is returned
|
* If no email configuration is provided, an ethereal email test account is returned
|
||||||
*/
|
*/
|
||||||
export const createNodemailerAdapter = async (
|
export const nodemailerAdapter = async (
|
||||||
args?: NodemailerAdapterArgs,
|
args?: NodemailerAdapterArgs,
|
||||||
): Promise<NodemailerAdapter> => {
|
): Promise<NodemailerAdapter> => {
|
||||||
const { defaultFromAddress, defaultFromName, transport } = await buildEmail(args)
|
const { defaultFromAddress, defaultFromName, transport } = await buildEmail(args)
|
||||||
|
|
||||||
const adapter: NodemailerAdapter = {
|
const adapter: NodemailerAdapter = () => ({
|
||||||
defaultFromAddress,
|
defaultFromAddress,
|
||||||
defaultFromName,
|
defaultFromName,
|
||||||
sendEmail: async (message) => {
|
sendEmail: async (message) => {
|
||||||
@@ -35,14 +35,15 @@ export const createNodemailerAdapter = async (
|
|||||||
...message,
|
...message,
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
|
|
||||||
return adapter
|
return adapter
|
||||||
}
|
}
|
||||||
|
|
||||||
async function buildEmail(
|
async function buildEmail(emailConfig?: NodemailerAdapterArgs): Promise<{
|
||||||
emailConfig?: NodemailerAdapterArgs,
|
defaultFromAddress: string
|
||||||
): Promise<{ defaultFromAddress: string; defaultFromName: string; transport: Transporter }> {
|
defaultFromName: string
|
||||||
|
transport: Transporter
|
||||||
|
}> {
|
||||||
if (!emailConfig) {
|
if (!emailConfig) {
|
||||||
const transport = await createMockAccount(emailConfig)
|
const transport = await createMockAccount(emailConfig)
|
||||||
if (!transport) throw new InvalidConfiguration('Unable to create Nodemailer test account.')
|
if (!transport) throw new InvalidConfiguration('Unable to create Nodemailer test account.')
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { URL } from 'url'
|
|||||||
|
|
||||||
import type { Collection } from '../collections/config/types.js'
|
import type { Collection } from '../collections/config/types.js'
|
||||||
import type { SanitizedConfig } from '../config/types.js'
|
import type { SanitizedConfig } from '../config/types.js'
|
||||||
import type { EmailAdapter } from '../email/types.js'
|
import type { InitializedEmailAdapter } from '../email/types.js'
|
||||||
import type { Payload } from '../index.js'
|
|
||||||
import type { PayloadRequest } from '../types/index.js'
|
import type { PayloadRequest } from '../types/index.js'
|
||||||
import type { User, VerifyConfig } from './types.js'
|
import type { User, VerifyConfig } from './types.js'
|
||||||
|
|
||||||
@@ -11,13 +10,13 @@ type Args = {
|
|||||||
collection: Collection
|
collection: Collection
|
||||||
config: SanitizedConfig
|
config: SanitizedConfig
|
||||||
disableEmail: boolean
|
disableEmail: boolean
|
||||||
email: EmailAdapter<any, unknown>
|
email: InitializedEmailAdapter
|
||||||
req: PayloadRequest
|
req: PayloadRequest
|
||||||
token: string
|
token: string
|
||||||
user: User
|
user: User
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendVerificationEmail(args: Args): Promise<void> {
|
export async function sendVerificationEmail(args: Args): Promise<void> {
|
||||||
// Verify token from e-mail
|
// Verify token from e-mail
|
||||||
const {
|
const {
|
||||||
collection: { config: collectionConfig },
|
collection: { config: collectionConfig },
|
||||||
@@ -74,5 +73,3 @@ async function sendVerificationEmail(args: Args): Promise<void> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default sendVerificationEmail
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import type {
|
|||||||
} from '../config/types.js'
|
} from '../config/types.js'
|
||||||
|
|
||||||
import executeAccess from '../../auth/executeAccess.js'
|
import executeAccess from '../../auth/executeAccess.js'
|
||||||
import sendVerificationEmail from '../../auth/sendVerificationEmail.js'
|
import { sendVerificationEmail } from '../../auth/sendVerificationEmail.js'
|
||||||
import { registerLocalStrategy } from '../../auth/strategies/local/register.js'
|
import { registerLocalStrategy } from '../../auth/strategies/local/register.js'
|
||||||
import { afterChange } from '../../fields/hooks/afterChange/index.js'
|
import { afterChange } from '../../fields/hooks/afterChange/index.js'
|
||||||
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { I18nOptions, TFunction } from '@payloadcms/translations'
|
import type { I18nOptions, TFunction } from '@payloadcms/translations'
|
||||||
import type { Options as ExpressFileUploadOptions } from 'express-fileupload'
|
import type { Options as ExpressFileUploadOptions } from 'express-fileupload'
|
||||||
import type GraphQL from 'graphql'
|
import type GraphQL from 'graphql'
|
||||||
import type { DestinationStream, LoggerOptions } from 'pino'
|
import type { DestinationStream, LoggerOptions, P } from 'pino'
|
||||||
import type React from 'react'
|
import type React from 'react'
|
||||||
import type { default as sharp } from 'sharp'
|
import type { default as sharp } from 'sharp'
|
||||||
import type { DeepRequired } from 'ts-essentials'
|
import type { DeepRequired } from 'ts-essentials'
|
||||||
@@ -16,7 +16,7 @@ import type {
|
|||||||
SanitizedCollectionConfig,
|
SanitizedCollectionConfig,
|
||||||
} from '../collections/config/types.js'
|
} from '../collections/config/types.js'
|
||||||
import type { DatabaseAdapterResult } from '../database/types.js'
|
import type { DatabaseAdapterResult } from '../database/types.js'
|
||||||
import type { EmailAdapter, SendMailOptions } from '../email/types.js'
|
import type { EmailAdapter, SendEmailOptions } from '../email/types.js'
|
||||||
import type { GlobalConfig, Globals, SanitizedGlobalConfig } from '../globals/config/types.js'
|
import type { GlobalConfig, Globals, SanitizedGlobalConfig } from '../globals/config/types.js'
|
||||||
import type { Payload } from '../index.js'
|
import type { Payload } from '../index.js'
|
||||||
import type { PayloadRequest, Where } from '../types/index.js'
|
import type { PayloadRequest, Where } from '../types/index.js'
|
||||||
@@ -531,7 +531,7 @@ export type Config = {
|
|||||||
*
|
*
|
||||||
* @see https://payloadcms.com/docs/email/overview
|
* @see https://payloadcms.com/docs/email/overview
|
||||||
*/
|
*/
|
||||||
email?: EmailAdapter<any, unknown> | Promise<EmailAdapter<any, unknown>>
|
email?: EmailAdapter | Promise<EmailAdapter>
|
||||||
/** Custom REST endpoints */
|
/** Custom REST endpoints */
|
||||||
endpoints?: Endpoint[]
|
endpoints?: Endpoint[]
|
||||||
/**
|
/**
|
||||||
@@ -690,3 +690,5 @@ export type EntityDescription =
|
|||||||
| EntityDescriptionFunction
|
| EntityDescriptionFunction
|
||||||
| Record<string, string>
|
| Record<string, string>
|
||||||
| string
|
| string
|
||||||
|
|
||||||
|
export type { EmailAdapter, SendEmailOptions }
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { EmailAdapter } from './types.js'
|
import type { InitializedEmailAdapter } from './types.js'
|
||||||
|
|
||||||
export const emailDefaults: Pick<
|
export const emailDefaults: Pick<
|
||||||
EmailAdapter<any, unknown>,
|
InitializedEmailAdapter,
|
||||||
'defaultFromAddress' | 'defaultFromName'
|
'defaultFromAddress' | 'defaultFromName'
|
||||||
> = {
|
> = {
|
||||||
defaultFromAddress: 'info@payloadcms.com',
|
defaultFromAddress: 'info@payloadcms.com',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { SendMailOptions } from 'nodemailer'
|
import type { SendEmailOptions } from './types.js'
|
||||||
|
|
||||||
export const getStringifiedToAddress = (message: SendMailOptions): string | undefined => {
|
export const getStringifiedToAddress = (message: SendEmailOptions): string | undefined => {
|
||||||
let stringifiedTo: string | undefined
|
let stringifiedTo: string | undefined
|
||||||
|
|
||||||
if (typeof message.to === 'string') {
|
if (typeof message.to === 'string') {
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import type { SendMailOptions } from 'nodemailer'
|
|
||||||
|
|
||||||
import type { Payload } from '../types/index.js'
|
import type { Payload } from '../types/index.js'
|
||||||
|
import type { SendEmailOptions } from './types.js'
|
||||||
|
|
||||||
import { getStringifiedToAddress } from './getStringifiedToAddress.js'
|
import { getStringifiedToAddress } from './getStringifiedToAddress.js'
|
||||||
|
|
||||||
export async function sendEmail(this: Payload, message: SendMailOptions): Promise<unknown> {
|
export async function sendEmail(this: Payload, message: SendEmailOptions): Promise<unknown> {
|
||||||
let result
|
let result
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
import type { SendMailOptions } from 'nodemailer'
|
|
||||||
|
|
||||||
import type { Payload } from '../index.js'
|
|
||||||
import type { EmailAdapter } from './types.js'
|
import type { EmailAdapter } from './types.js'
|
||||||
|
|
||||||
import { emailDefaults } from './defaults.js'
|
import { emailDefaults } from './defaults.js'
|
||||||
import { getStringifiedToAddress } from './getStringifiedToAddress.js'
|
import { getStringifiedToAddress } from './getStringifiedToAddress.js'
|
||||||
|
|
||||||
export type StdoutAdapter = EmailAdapter<SendMailOptions, void>
|
export const stdoutAdapter: EmailAdapter<void> = ({ payload }) => ({
|
||||||
|
|
||||||
export const createStdoutAdapter = (payload: Payload) => {
|
|
||||||
const stdoutAdapter: StdoutAdapter = {
|
|
||||||
defaultFromAddress: emailDefaults.defaultFromAddress,
|
defaultFromAddress: emailDefaults.defaultFromAddress,
|
||||||
defaultFromName: emailDefaults.defaultFromName,
|
defaultFromName: emailDefaults.defaultFromName,
|
||||||
sendEmail: async (message) => {
|
sendEmail: async (message) => {
|
||||||
@@ -18,6 +12,4 @@ export const createStdoutAdapter = (payload: Payload) => {
|
|||||||
payload.logger.info({ msg: res })
|
payload.logger.info({ msg: res })
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
},
|
},
|
||||||
}
|
})
|
||||||
return stdoutAdapter
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,12 +1,31 @@
|
|||||||
import type { SendMailOptions } from 'nodemailer'
|
import type { SendMailOptions as NodemailerSendMailOptions } from 'nodemailer'
|
||||||
|
|
||||||
export type { SendMailOptions }
|
import type { Payload } from '../types/index.js'
|
||||||
|
|
||||||
export type EmailAdapter<
|
type Prettify<T> = {
|
||||||
TSendEmailOptions extends SendMailOptions,
|
[K in keyof T]: T[K]
|
||||||
KSendEmailResponse = unknown,
|
} & NonNullable<unknown>
|
||||||
> = {
|
|
||||||
|
/**
|
||||||
|
* Options for sending an email. Allows access to the PayloadRequest object
|
||||||
|
*/
|
||||||
|
export type SendEmailOptions = Prettify<NodemailerSendMailOptions>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email adapter after it has been initialized. This is used internally by Payload.
|
||||||
|
*/
|
||||||
|
export type InitializedEmailAdapter<TSendEmailResponse = unknown> = ReturnType<
|
||||||
|
EmailAdapter<TSendEmailResponse>
|
||||||
|
>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Email adapter interface. Allows a generic type for the response of the sendEmail method.
|
||||||
|
*
|
||||||
|
* This is the interface to use if you are creating a new email adapter.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type EmailAdapter<TSendEmailResponse = unknown> = ({ payload }: { payload: Payload }) => {
|
||||||
defaultFromAddress: string
|
defaultFromAddress: string
|
||||||
defaultFromName: string
|
defaultFromName: string
|
||||||
sendEmail: (message: TSendEmailOptions) => Promise<KSendEmailResponse>
|
sendEmail: (message: SendEmailOptions) => Promise<TSendEmailResponse>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export type * from '../uploads/types.js'
|
|||||||
export type { DocumentPermissions, FieldPermissions } from '../auth/index.js'
|
export type { DocumentPermissions, FieldPermissions } from '../auth/index.js'
|
||||||
|
|
||||||
export type { MeOperationResult } from '../auth/operations/me.js'
|
export type { MeOperationResult } from '../auth/operations/me.js'
|
||||||
export type { EmailAdapter, SendMailOptions } from '../email/types.js'
|
export type { EmailAdapter as PayloadEmailAdapter, SendEmailOptions } from '../email/types.js'
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
CollapsedPreferences,
|
CollapsedPreferences,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import type { ExecutionResult, GraphQLSchema, ValidationRule } from 'graphql'
|
import type { ExecutionResult, GraphQLSchema, ValidationRule } from 'graphql'
|
||||||
import type { OperationArgs, Request as graphQLRequest } from 'graphql-http'
|
import type { OperationArgs, Request as graphQLRequest } from 'graphql-http'
|
||||||
import type { SendMailOptions } from 'nodemailer'
|
|
||||||
import type pino from 'pino'
|
import type pino from 'pino'
|
||||||
|
|
||||||
import crypto from 'crypto'
|
import crypto from 'crypto'
|
||||||
@@ -35,7 +34,7 @@ import type {
|
|||||||
} from './collections/operations/local/update.js'
|
} from './collections/operations/local/update.js'
|
||||||
import type { InitOptions, SanitizedConfig } from './config/types.js'
|
import type { InitOptions, SanitizedConfig } from './config/types.js'
|
||||||
import type { BaseDatabaseAdapter, PaginatedDocs } from './database/types.js'
|
import type { BaseDatabaseAdapter, PaginatedDocs } from './database/types.js'
|
||||||
import type { EmailAdapter } from './email/types.js'
|
import type { InitializedEmailAdapter } from './email/types.js'
|
||||||
import type { TypeWithID as GlobalTypeWithID, Globals } from './globals/config/types.js'
|
import type { TypeWithID as GlobalTypeWithID, Globals } from './globals/config/types.js'
|
||||||
import type { Options as FindGlobalOptions } from './globals/operations/local/findOne.js'
|
import type { Options as FindGlobalOptions } from './globals/operations/local/findOne.js'
|
||||||
import type { Options as FindGlobalVersionByIDOptions } from './globals/operations/local/findVersionByID.js'
|
import type { Options as FindGlobalVersionByIDOptions } from './globals/operations/local/findVersionByID.js'
|
||||||
@@ -49,7 +48,7 @@ import { APIKeyAuthentication } from './auth/strategies/apiKey.js'
|
|||||||
import { JWTAuthentication } from './auth/strategies/jwt.js'
|
import { JWTAuthentication } from './auth/strategies/jwt.js'
|
||||||
import localOperations from './collections/operations/local/index.js'
|
import localOperations from './collections/operations/local/index.js'
|
||||||
import { validateSchema } from './config/validate.js'
|
import { validateSchema } from './config/validate.js'
|
||||||
import { createStdoutAdapter } from './email/stdoutAdapter.js'
|
import { stdoutAdapter } from './email/stdoutAdapter.js'
|
||||||
import { fieldAffectsData } from './exports/types.js'
|
import { fieldAffectsData } from './exports/types.js'
|
||||||
import localGlobalOperations from './globals/operations/local/index.js'
|
import localGlobalOperations from './globals/operations/local/index.js'
|
||||||
import flattenFields from './utilities/flattenTopLevelFields.js'
|
import flattenFields from './utilities/flattenTopLevelFields.js'
|
||||||
@@ -102,8 +101,7 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
|||||||
return duplicate<T>(this, options)
|
return duplicate<T>(this, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do these types need to be injected via GeneratedTypes?
|
email: InitializedEmailAdapter
|
||||||
email: EmailAdapter<any, unknown>
|
|
||||||
|
|
||||||
// TODO: re-implement or remove?
|
// TODO: re-implement or remove?
|
||||||
// errorHandler: ErrorHandler
|
// errorHandler: ErrorHandler
|
||||||
@@ -252,7 +250,7 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
|||||||
|
|
||||||
secret: string
|
secret: string
|
||||||
|
|
||||||
sendEmail: (message: SendMailOptions) => Promise<unknown>
|
sendEmail: InitializedEmailAdapter['sendEmail']
|
||||||
|
|
||||||
types: {
|
types: {
|
||||||
arrayTypes: any
|
arrayTypes: any
|
||||||
@@ -366,18 +364,19 @@ export class BasePayload<TGeneratedTypes extends GeneratedTypes> {
|
|||||||
|
|
||||||
// Load email adapter
|
// Load email adapter
|
||||||
if (this.config.email instanceof Promise) {
|
if (this.config.email instanceof Promise) {
|
||||||
this.email = await this.config.email
|
const awaitedAdapter = await this.config.email
|
||||||
|
this.email = awaitedAdapter({ payload: this })
|
||||||
} else if (this.config.email) {
|
} else if (this.config.email) {
|
||||||
this.email = this.config.email
|
this.email = this.config.email({ payload: this })
|
||||||
} else {
|
} else {
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
`No email adapter provided. Email will be written to stdout. More info at https://payloadcms.com/docs/email/overview.`,
|
`No email adapter provided. Email will be written to stdout. More info at https://payloadcms.com/docs/email/overview.`,
|
||||||
)
|
)
|
||||||
|
|
||||||
this.email = createStdoutAdapter(this)
|
this.email = stdoutAdapter({ payload: this })
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sendEmail = this.email.sendEmail
|
this.sendEmail = this.email['sendEmail']
|
||||||
|
|
||||||
serverInitTelemetry(this)
|
serverInitTelemetry(this)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Config } from 'payload/config'
|
import type { Config } from 'payload/config'
|
||||||
|
import type { Payload } from 'payload'
|
||||||
import nodemailer from 'nodemailer'
|
import nodemailer from 'nodemailer'
|
||||||
|
|
||||||
import { defaults } from 'payload/config'
|
import { defaults } from 'payload/config'
|
||||||
@@ -12,6 +13,8 @@ describe('email', () => {
|
|||||||
const apiKey = 'test'
|
const apiKey = 'test'
|
||||||
let createTransportSpy: jest.SpyInstance
|
let createTransportSpy: jest.SpyInstance
|
||||||
|
|
||||||
|
const mockedPayload: Payload = jest.fn() as unknown as Payload
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
defaultConfig = defaults as Config
|
defaultConfig = defaults as Config
|
||||||
|
|
||||||
@@ -71,8 +74,10 @@ describe('email', () => {
|
|||||||
defaultFromAddress,
|
defaultFromAddress,
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(email.defaultFromName).toEqual(defaultFromName)
|
const initializedEmail = email({ payload: mockedPayload })
|
||||||
expect(email.defaultFromAddress).toEqual(defaultFromAddress)
|
|
||||||
|
expect(initializedEmail.defaultFromName).toEqual(defaultFromName)
|
||||||
|
expect(initializedEmail.defaultFromAddress).toEqual(defaultFromAddress)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { NodemailerAdapter } from '@payloadcms/email-nodemailer'
|
import type { NodemailerAdapter } from '@payloadcms/email-nodemailer'
|
||||||
|
|
||||||
import { createNodemailerAdapter } from '@payloadcms/email-nodemailer'
|
import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
|
||||||
import nodemailer from 'nodemailer'
|
import nodemailer from 'nodemailer'
|
||||||
|
|
||||||
import type { PayloadCloudEmailOptions } from './types.js'
|
import type { PayloadCloudEmailOptions } from './types.js'
|
||||||
@@ -12,7 +12,7 @@ export const payloadCloudEmail = async (
|
|||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!args.apiKey) throw new Error('apiKey must be provided to use Payload Cloud Email ')
|
if (!args.apiKey) throw new Error('apiKey must be provided to use Payload Cloud Email')
|
||||||
if (!args.defaultDomain)
|
if (!args.defaultDomain)
|
||||||
throw new Error('defaultDomain must be provided to use Payload Cloud Email')
|
throw new Error('defaultDomain must be provided to use Payload Cloud Email')
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ export const payloadCloudEmail = async (
|
|||||||
const defaultFromAddress =
|
const defaultFromAddress =
|
||||||
args.defaultFromAddress || `cms@${customDomains.length ? customDomains[0] : defaultDomain}`
|
args.defaultFromAddress || `cms@${customDomains.length ? customDomains[0] : defaultDomain}`
|
||||||
|
|
||||||
const emailAdapter = await createNodemailerAdapter({
|
const emailAdapter = await nodemailerAdapter({
|
||||||
defaultFromAddress,
|
defaultFromAddress,
|
||||||
defaultFromName,
|
defaultFromName,
|
||||||
skipVerify,
|
skipVerify,
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import type { Config } from 'payload/config'
|
import type { Config } from 'payload/config'
|
||||||
|
import type { Payload } from 'payload'
|
||||||
|
|
||||||
import nodemailer from 'nodemailer'
|
import nodemailer from 'nodemailer'
|
||||||
import { defaults } from 'payload/config'
|
import { defaults } from 'payload/config'
|
||||||
|
|
||||||
import { payloadCloud } from './plugin.js'
|
import { payloadCloud } from './plugin.js'
|
||||||
import { createNodemailerAdapter } from '@payloadcms/email-nodemailer'
|
import { NodemailerAdapter, nodemailerAdapter } from '@payloadcms/email-nodemailer'
|
||||||
|
|
||||||
|
const mockedPayload: Payload = jest.fn() as unknown as Payload
|
||||||
|
|
||||||
describe('plugin', () => {
|
describe('plugin', () => {
|
||||||
let createTransportSpy: jest.SpyInstance
|
let createTransportSpy: jest.SpyInstance
|
||||||
@@ -12,13 +15,16 @@ describe('plugin', () => {
|
|||||||
const skipVerify = true
|
const skipVerify = true
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
createTransportSpy = jest.spyOn(nodemailer, 'createTransport').mockImplementation(() => {
|
createTransportSpy = jest.spyOn(nodemailer, 'createTransport').mockImplementationOnce(() => {
|
||||||
return {
|
return {
|
||||||
verify: jest.fn(),
|
verify: jest.fn(),
|
||||||
|
transporter: {
|
||||||
|
name: 'Nodemailer - SMTP',
|
||||||
|
},
|
||||||
} as unknown as ReturnType<typeof nodemailer.createTransport>
|
} as unknown as ReturnType<typeof nodemailer.createTransport>
|
||||||
})
|
})
|
||||||
|
|
||||||
const createTestAccountSpy = jest.spyOn(nodemailer, 'createTestAccount').mockResolvedValue({
|
const createTestAccountSpy = jest.spyOn(nodemailer, 'createTestAccount').mockResolvedValueOnce({
|
||||||
pass: 'password',
|
pass: 'password',
|
||||||
user: 'user',
|
user: 'user',
|
||||||
web: 'ethereal.email',
|
web: 'ethereal.email',
|
||||||
@@ -67,7 +73,11 @@ describe('plugin', () => {
|
|||||||
const plugin = payloadCloud()
|
const plugin = payloadCloud()
|
||||||
const config = await plugin(createConfig())
|
const config = await plugin(createConfig())
|
||||||
|
|
||||||
assertCloudEmail(config)
|
expect(createTransportSpy).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
host: 'smtp.resend.com',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// eslint-disable-next-line jest/expect-expect
|
// eslint-disable-next-line jest/expect-expect
|
||||||
@@ -102,7 +112,7 @@ describe('plugin', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const configWithTransport = createConfig({
|
const configWithTransport = createConfig({
|
||||||
email: await createNodemailerAdapter({
|
email: await nodemailerAdapter({
|
||||||
defaultFromAddress: 'test@test.com',
|
defaultFromAddress: 'test@test.com',
|
||||||
defaultFromName: 'Test',
|
defaultFromName: 'Test',
|
||||||
transport: existingTransport,
|
transport: existingTransport,
|
||||||
@@ -124,7 +134,7 @@ describe('plugin', () => {
|
|||||||
const defaultFromName = 'Test'
|
const defaultFromName = 'Test'
|
||||||
const defaultFromAddress = 'test@test.com'
|
const defaultFromAddress = 'test@test.com'
|
||||||
const configWithPartialEmail = createConfig({
|
const configWithPartialEmail = createConfig({
|
||||||
email: await createNodemailerAdapter({
|
email: await nodemailerAdapter({
|
||||||
defaultFromAddress,
|
defaultFromAddress,
|
||||||
defaultFromName,
|
defaultFromName,
|
||||||
skipVerify,
|
skipVerify,
|
||||||
@@ -133,12 +143,18 @@ describe('plugin', () => {
|
|||||||
|
|
||||||
const plugin = payloadCloud()
|
const plugin = payloadCloud()
|
||||||
const config = await plugin(configWithPartialEmail)
|
const config = await plugin(configWithPartialEmail)
|
||||||
const emailConfig = config.email as Awaited<ReturnType<typeof createNodemailerAdapter>>
|
const emailConfig = config.email as Awaited<ReturnType<typeof nodemailerAdapter>>
|
||||||
|
|
||||||
expect(emailConfig.defaultFromName).toEqual(defaultFromName)
|
const initializedEmail = emailConfig({ payload: mockedPayload })
|
||||||
expect(emailConfig.defaultFromAddress).toEqual(defaultFromAddress)
|
|
||||||
|
|
||||||
assertCloudEmail(config)
|
expect(initializedEmail.defaultFromName).toEqual(defaultFromName)
|
||||||
|
expect(initializedEmail.defaultFromAddress).toEqual(defaultFromAddress)
|
||||||
|
|
||||||
|
expect(createTransportSpy).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
host: 'smtp.resend.com',
|
||||||
|
}),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -152,10 +168,8 @@ function assertNoCloudStorage(config: Config) {
|
|||||||
expect(config.upload?.useTempFiles).toBeFalsy()
|
expect(config.upload?.useTempFiles).toBeFalsy()
|
||||||
}
|
}
|
||||||
|
|
||||||
function assertCloudEmail(config: Config) {
|
async function assertCloudEmail(config: Config) {
|
||||||
expect(
|
expect(config.email && 'name' in config.email).toStrictEqual('Nodemailer - SMTP')
|
||||||
config.email && 'sendEmail' in config.email && typeof config.email.sendEmail === 'function',
|
|
||||||
).toBe(true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createConfig(overrides?: Partial<Config>): Config {
|
function createConfig(overrides?: Partial<Config>): Config {
|
||||||
|
|||||||
Reference in New Issue
Block a user