bde148f286
* wip * some extra fixes * stuff kinda works? * need to figure out mocks * need to figure out mocks * fix db listener * gqlgen fix * minor gqlgen watch adjustment * lint fixes * delete old codegen file * converting migrations to ESM * getModuleDIrectory * vitest sort of works * added back ts-vitest * resolve gql double load * fixing test timeout configs * TSC lint fix * fix automate tests * moar debugging * debugging * more debugging * codegen update * server works * yargs migrated * chore(server): getting rid of global mocks for Server ESM (#5046) * got rid of email mock * got rid of comment mocks * got rid of multi region mocks * got rid of stripe mock * admin override mock updated * removed final mock * fixing import.meta.resolve calls * another import.meta.resolve fix * added requested test * nyc ESM fix * removed unneeded deps + linting * yarn lock forgot to commit * tryna fix flakyness * email capture util fix * sendEmail fix * fix TSX check * sender transporter fix + CR comments * merge main fix * test fixx * circleci fix * gqlgen bigint fix * error formatter fix * more error formatting improvements * esmloader added to Dockerfile * more dockerfile fixes * bg jobs fix
104 lines
2.4 KiB
TypeScript
104 lines
2.4 KiB
TypeScript
import { EmailsEvents } from '@/modules/emails/domain/events'
|
|
import { getEventBus } from '@/modules/shared/services/eventBus'
|
|
import { MaybeAsync } from '@speckle/shared'
|
|
import type Mail from 'nodemailer/lib/mailer'
|
|
|
|
type ListenOptions = {
|
|
handler?: (email: Mail.Options) => MaybeAsync<void>
|
|
times?: number
|
|
}
|
|
|
|
export const createEmailListener = async (
|
|
options?: Partial<{
|
|
destroyWhenNoListeners: boolean
|
|
}>
|
|
) => {
|
|
const eventBus = getEventBus()
|
|
let collectedSends: Mail.Options[] = []
|
|
let listenerQuitters: (() => void)[] = []
|
|
|
|
// Global listener, tracks emails even if no listen() invoked
|
|
const quitGlobal = eventBus.listen(
|
|
EmailsEvents.PreparingToSend,
|
|
async ({ payload }) => {
|
|
collectedSends.push(payload.options)
|
|
}
|
|
)
|
|
|
|
/**
|
|
* Reset .listen() calls and collected sends (by default)
|
|
*/
|
|
const reset = (
|
|
options?: Partial<{
|
|
listenersOnly: boolean
|
|
}>
|
|
) => {
|
|
listenerQuitters.forEach((quit) => quit())
|
|
listenerQuitters = []
|
|
|
|
if (!options?.listenersOnly) {
|
|
collectedSends = []
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Close all listeners
|
|
*/
|
|
const destroy = async () => {
|
|
quitGlobal()
|
|
reset({ listenersOnly: true })
|
|
}
|
|
|
|
/**
|
|
* Start a listening session w/ localized collected sends
|
|
*/
|
|
const listen = (params: ListenOptions) => {
|
|
let timesReceived = 0
|
|
const localSends: Mail.Options[] = []
|
|
|
|
const quit = eventBus.listen(EmailsEvents.PreparingToSend, async ({ payload }) => {
|
|
await params.handler?.(payload.options)
|
|
localSends.push(payload.options)
|
|
timesReceived += 1
|
|
|
|
if (params.times && timesReceived >= params.times) {
|
|
await wrappedQuit()
|
|
}
|
|
})
|
|
|
|
const wrappedQuit = async () => {
|
|
quit()
|
|
listenerQuitters.splice(listenerQuitters.indexOf(wrappedQuit), 1)
|
|
|
|
// Destroy all listeners, if last one
|
|
if (options?.destroyWhenNoListeners && listenerQuitters.length === 0) {
|
|
await destroy()
|
|
}
|
|
}
|
|
|
|
listenerQuitters.push(wrappedQuit)
|
|
|
|
return {
|
|
/**
|
|
* Get sends collected during this listening session.
|
|
*/
|
|
getSends: () => localSends.slice(),
|
|
quit: wrappedQuit
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all sends collected (even outside of listener sessions)
|
|
*/
|
|
const getSends = () => collectedSends.slice()
|
|
|
|
return {
|
|
destroy,
|
|
listen,
|
|
getSends,
|
|
reset
|
|
}
|
|
}
|
|
|
|
export type TestEmailListener = Awaited<ReturnType<typeof createEmailListener>>
|