2

I do have an E2E test for retrieving confirmation email after registration. I need the link in the confirmation email to login the user as the link in the email contains the token etc. for access.

The test runs fine, email is being sent to user, but when I try to get email and its content including link, it just doesn't invoke the function (callback) for it which causes the following error:

- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

The test is based on mailListener and following this posting which is quite helpful.

Here is the helper function which should be invoked and retrieve the mail with content. It's being embedded within spec.ts file before starting the suite describe():

function getLastEmail() {
    const deferred = protractor.promise.defer();
    console.log("Waiting for an email...");

    mailListener.on("mail", function(mail){
        console.log('Email received, parsing...');
        deferred.fulfill(mail);
    });
    return deferred.promise;
}

and here is the test where the function getLastEmail() is being called:

it('should retrieve email link and login with a registration code sent to an email', function () {
     browser.get("/login");
     browser.controlFlow().wait(getLastEmail())
         .then((email) => {
             expect(email['subject']).toEqual("Email subject");  
             expect(email['headers'].to).toEqual("myName@yyyy.com");
             console.log(email['text']);
         });
});

And here is the initialization of the Mail Listener which is also embedded in the spec.ts file in the top:

    // for confirmation email - added on 31.01.18
    var MailListener = require("mail-listener2");
    // here goes your email connection configuration
    var mailListener = new MailListener({
        username: "myName@yyyy.com",
        password: "myPassword",
        host: "imap.gmail.com",
        port: 993, // imap port 
        tls: true,
        tlsOptions: { rejectUnauthorized: false },
        mailbox: "INBOX", // mailbox to monitor 
        searchFilter: ["UNSEEN", "FLAGGED"], // the search filter being used after an IDLE notification has been retrieved 
        markSeen: true, // all fetched email willbe marked as seen and not fetched next time 
        fetchUnreadOnStart: true, // use it only if you want to get all unread email on lib start. Default is `false`, 
        mailParserOptions: { streamAttachments: true }, // options to be passed to mailParser lib. 
        // attachments: true, // download attachments as they are encountered to the project directory 
        // attachmentOptions: { directory: "attachments/" } // specify a download directory for attachments 
    });

    mailListener.start();

    mailListener.on("server:connected", function(){
        console.log("Mail listener initialized");
    });

    mailListener = mailListener;`

Can it be that the email credentials are not correct or the host name is wrong as in this case I would expect an error? What do I have to change to force the function: getLastEmail() to be invoked?

  • I did few changes on var mailListener = new MailListener({.... and now I get an output saying: <= '* 55 EXISTS' => DONE <= 'IDLE OK IDLE terminated (Success)' => 'A5 UID SEARCH UNSEEN FLAGGED' <= '* SEARCH' <= 'A5 OK SEARCH completed (Success)' => 'IDLE IDLE' <= '+ idling', but to be honest I really can't figure out what the output exactly means and I still get the error: - Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.. Any help pls.? I'am stuck with this issue! – k.vincent Feb 5 '18 at 13:16
  • I tried above code its not fetching mails for me – paramveer Aug 27 at 6:41
  • @paramveer: Please double check your Email account access etc. Do you get an error? – k.vincent Aug 27 at 12:47
1

Issue fixed. I think this posting here is really too old and just doesn't work as it should be. First of all here is the one which would run without issues, and you also find the comments which explain what each function does etc.

Code in protractor.config.js added in onPrepare() method looks as following:

var MailListener = require("mail-listener2");

    var mailListener = new MailListener({
        username: "username", // email or userName
        password: "password", // password
        host: "your.emailserver.port", // e.g. imap.gmail.com
        port: 993,
        tls: true,
        fetchUnreadOnStart: true
    });
    mailListener.on("server:connected", function() {
        console.log("imapConnected");
    });
    mailListener.on("server:disconnected", function() {
        console.log("imapDisconnected");
    });
    mailListener.on("error", function(err) {
        console.log(err);
    });

    var count = 0;
    mailListener.on("mail", function(mail, seqno, attributes) {
        var mailuid = attributes.uid;
        var toMailbox = '[Gmail]/All Mail';
        var i = ++count;
        if (i > 2) {
            mailListener.stop(); // start listening
            return;
        }
        console.log('attempting to mark msg read/seen');
        mailListener.imap.addFlags(mailuid, '\\Seen', function(err) {
            if (err) {
                console.log('error marking message read/SEEN');
                return;
            }
            console.log('moving ' + (seqno || '?') + ' to ' + toMailbox);
            mailListener.imap.move(mailuid, toMailbox, function(err) {
                if (err) {
                    console.log('error moving message');
                    return;
                }
                console.log('moved ' + (seqno || '?'), mail.subject);
            });
        });
    });

    mailListener.start(); // start listening
    setTimeout(function() {
        mailListener.stop(); // start listening
    }, 60 * 1000);

    global.mailListener = mailListener;

Then after that, just adding a helper function after onPrepare() method like:

params: {
    getLastEmail: function() {
        const deferred = protractor.promise.defer();
        console.log("Waiting for email...");

        var count = 0;
        mailListener.on("mail", function(mail, seqno, attributes) {
            var mailuid = attributes.uid;
            var toMailbox = '[Gmail]/All Mail';
            var i = ++count;
            if (i > 2) {
                mailListener.stop(); // start listening
                return;
            }
            testContent = {
                i: i,
                subject: mail.subject,
                seqno: seqno,
                uid: attributes.uid,
                attributes: attributes,
                text: mail.html
            };
            deferred.fulfill(mail);
        });
        return deferred.promise;
    }
},

Then move to *.spec.ts file and call/invoke the helper function inside the test case etc. as following:

it('should do something', () => {
    browser.wait(browser.params.getLastEmail()).then( (email) => {....})

})

It's working for me straight forward and I wanted to share this with you guys as when searching I did see that a lot of users are struggling with mailListener feature/library.

  • is it a requirement to have a gmail server? I was looking into using a local smtp for development for e2e tests for mails sent from .NET core API – umutesen Apr 29 at 11:56
  • No, it's not a requirement. Should actually work with a local SMTP on local dev environment. But myself, I didn't try that. – k.vincent May 1 at 15:18

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.