0

I have a Namecheap PrivateEmail.com account that I am trying to use to send verification codes to my users upon signup. I am trying to test the send email function using a small integration test.

I can confirm that the email account itself can send and receive emails. I confirmed by sending and receiving an email.

I have a collection of research I've been using:

BLOG: How to send a magic link with Nodemailer and Namecheap

https://nodemailer.com/smtp/ <= docs

StackOverflow: Send Nodemailer e-mail with Namecheap email

My code is as follows

// in a test file
            const connectionEstablished = await verifySMTPConnection();
            expect(connectionEstablished).toBe(true);
            const myEmail = process.env.MY_EMAIL ? process.env.MY_EMAIL : "fail";
            const senderEmail = process.env.PRIVATE_EMAIL;
            expect(senderEmail).toBeDefined();
            if (myEmail == "fail") {
                throw Error("fail to load email from env");
            }
            const payload = { from: senderEmail, to: myEmail, subject: "Test email!", html: "foo!" };
            sendEmail(payload);
            console.warn("You just sent a real email"); // this logs!
            expect(true).toBe(false); // intentional failure to highlight that the test has run.

Here are my functions

async function sendEmail({ to, subject, html, from = config.emailFrom }: ISendEmail) {
    const transporter = nodemailer.createTransport({
        host: config.emailHost,
        port: config.emailPort,
        auth: { user: config.emailUser, pass: config.emailPassword },
    });
    console.log("Sending email to", to);
    console.warn("Email might be enabled");
    await transporter.sendMail({ from, to, subject, html });
}

export function verifySMTPConnection(): Promise<boolean> {
    const transporter = nodemailer.createTransport({
        host: config.emailHost,
        port: config.emailPort,
        auth: { user: config.emailUser, pass: config.emailPassword },
    });
    return new Promise((resolve, reject) => {
        transporter.verify(function (error, success) {
            if (error) {
                console.log(error);
                resolve(false);
            } else {
                console.log("Server is ready to take our messages");
                resolve(true); // I get the "true" response every time
            }
        });
    });
}

export default sendEmail;

And the code for the config:


const emailFrom = process.env.PRIVATE_EMAIL ? process.env.PRIVATE_EMAIL : failure;
const emailHost = process.env.PRIVATE_EMAIL_HOST ? process.env.PRIVATE_EMAIL_HOST : failure;
const emailPort = process.env.PRIVATE_EMAIL_PORT ? parseInt(process.env.PRIVATE_EMAIL_PORT, 10) : 465;
const emailUser = process.env.PRIVATE_EMAIL_USER ? process.env.PRIVATE_EMAIL_USER : failure;
const emailPassword = process.env.PRIVATE_EMAIL_PASSWORD ? process.env.PRIVATE_EMAIL_PASSWORD : failure;

if (emailFrom == failure || emailHost == failure || emailUser == failure || emailPassword == failure) {
    throw Error("Failed to load private email details");
}

export default { emailFrom, emailHost, emailPort, emailUser, emailPassword };

I can confirm that:

  1. my PRIVATE_EMAIL field really is my desired email account
  2. PRIVATE_EMAIL_HOST=mail.privateemail.com is correct as per this post
  3. PRIVATE_EMAIL_PORT=465 is correct as per here on freecodecamp (I tried 587 as well)
  4. PRIVATE_EMAIL_USER and PRIVATE_EMAIL_PASSWORD are both correct.

changing the password to be incorrect gives the error: Error: Invalid login: 535 5.7.8 Error: authentication failed: which leads me to believe that since resolve(true); occurs when I have the correct pw, I have the correct credentials and the connection is established. The docs say "Be aware though that this call only tests connection and authentication but it does not check if the service allows you to use a specific envelope From address or not" but that seems irrelevant.

Could there be something in PrivateEmail settings I am missing?

plutownium
  • 1,220
  • 2
  • 9
  • 23

1 Answers1

0

I suspect the problem is that either (a) I had to wait some amount of time for DNS settings to propagate, or more likely (b) I was ending the sendEmail process too early for the email sending to complete!

This code worked

// in my test file
            const x = new Promise((resolve, reject) => {
                sendEmail(payload, resolve);
            });
            await x;
// in my sendEmail file

async function sendEmail({ to, subject, html, from = config.emailFrom }: ISendEmail, resolve?: Function) {
    const transporter = nodemailer.createTransport({
        host: config.emailHost,
        port: config.emailPort,
        auth: { user: config.emailUser, pass: config.emailPassword },
    });
    console.log("Sending email to", to);
    console.warn("Email might be enabled");
    await transporter.sendMail({ from, to, subject, html }, function (error, info) {
        if (error) {
            console.log(error);
        } else {
            console.log("Email sent: ", info);
        }
        if (resolve) {
            resolve(true);
        }
    });
}

when I awaited the resolve it worked!

plutownium
  • 1,220
  • 2
  • 9
  • 23