Files
speckle-server/packages/server/test/speckle-helpers/email.ts
T
Kristaps Fabians Geikins bde148f286 chore(server): migrating fully to ESM (#5042)
* 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
2025-07-14 10:26:19 +03:00

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>>