diff --git a/lib/emails/invitation.ts b/lib/emails/invitation.ts index f4e8be8f..b95d7790 100644 --- a/lib/emails/invitation.ts +++ b/lib/emails/invitation.ts @@ -2,20 +2,31 @@ import nodemailer from "nodemailer"; import { serverConfig } from "../serverConfig"; -export default function createInvitationEmail(data: any, options: any = {}) { - return sendEmail(data, { - provider: { - transport: serverConfig.transport, - from: serverConfig.from, - }, - ...options, - }); +export type Invitation = { + from?: string; + toEmail: string; + teamName: string; + token?: string; +}; + +type EmailProvider = { + from: string; + transport: any; +}; + +export function createInvitationEmail(data: Invitation) { + const provider = { + transport: serverConfig.transport, + from: serverConfig.from, + } as EmailProvider; + return sendEmail(data, provider); } -const sendEmail = (invitation: any, { provider }) => +const sendEmail = (invitation: Invitation, provider: EmailProvider): Promise => new Promise((resolve, reject) => { const { transport, from } = provider; + const invitationHtml = html(invitation); nodemailer.createTransport(transport).sendMail( { from: `Cal.com <${from}>`, @@ -23,8 +34,8 @@ const sendEmail = (invitation: any, { provider }) => subject: (invitation.from ? invitation.from + " invited you" : "You have been invited") + ` to join ${invitation.teamName}`, - html: html(invitation), - text: text(invitation), + html: invitationHtml, + text: text(invitationHtml), }, (error) => { if (error) { @@ -36,7 +47,7 @@ const sendEmail = (invitation: any, { provider }) => ); }); -const html = (invitation: any) => { +export function html(invitation: Invitation): string { let url: string = process.env.BASE_URL + "/settings/teams"; if (invitation.token) { url = `${process.env.BASE_URL}/auth/signup?token=${invitation.token}&callbackUrl=${url}`; @@ -82,10 +93,9 @@ const html = (invitation: any) => { ` ); -}; +} // just strip all HTML and convert
to \n -const text = (evt: any) => - html(evt) - .replace("
", "\n") - .replace(/<[^>]+>/g, ""); +export function text(htmlStr: string): string { + return htmlStr.replace("
", "\n").replace(/<[^>]+>/g, ""); +} diff --git a/pages/api/teams/[team]/invite.ts b/pages/api/teams/[team]/invite.ts index 336d4662..fa18b9da 100644 --- a/pages/api/teams/[team]/invite.ts +++ b/pages/api/teams/[team]/invite.ts @@ -3,7 +3,7 @@ import type { NextApiRequest, NextApiResponse } from "next"; import { getSession } from "@lib/auth"; -import createInvitationEmail from "../../../../lib/emails/invitation"; +import { createInvitationEmail } from "../../../../lib/emails/invitation"; import prisma from "../../../../lib/prisma"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { diff --git a/test/lib/emails/invitation.test.ts b/test/lib/emails/invitation.test.ts new file mode 100644 index 00000000..e291e850 --- /dev/null +++ b/test/lib/emails/invitation.test.ts @@ -0,0 +1,23 @@ +import { expect, it } from "@jest/globals"; + +import { html, text, Invitation } from "@lib/emails/invitation"; + +it("email text rendering should strip tags and add new lines", () => { + const result = text("

hello world


welcome to the brave new world"); + expect(result).toEqual("hello world\nwelcome to the brave new world"); +}); + +it("email html should render invite email", () => { + const invitation = { + from: "Huxley", + toEmail: "hello@example.com", + teamName: "Calendar Lovers", + token: "invite-token", + } as Invitation; + const result = html(invitation); + expect(result).toContain('
Huxley invited you to join the team "Calendar Lovers" in Cal.com.
'); + expect(result).toContain("/auth/signup?token=invite-token&"); + expect(result).toContain( + 'If you prefer not to use "hello@example.com" as your Cal.com email or already have a Cal.com account, please request another invitation to that email.' + ); +});