Search code examples
javascriptloopssetintervalnightwatch.js

Create loop in Javascript to use with imap-simple nodejs package


Firstly, some background as to what my test script will cover.

Pressing a button on a website will fire off an email to a test mailbox.

This email can take anything between 10 and 30 minutes to arrive in the test mailbox.

So using the following code from imap-simple ;

'get new email info': function(browser) {
    imaps.connect(config).then(function (connection) {
        return connection.openBox('INBOX').then(function () {
            var searchCriteria = ['UNSEEN'];
            var fetchOptions = {
                bodies: ['HEADER', 'TEXT'],
                markSeen: false
            };
            return connection.search(searchCriteria, fetchOptions).then(function (results) {
                var subjects = results.map(function (res) {
                    return res.parts.filter(function (part) {
                        return part.which === 'HEADER';
                        })[0].body.subject[0];
                });
            console.log(subjects);

Correctly comes back with a blank subjects array, as the email hasn't been received by the test mailbox yet.

Adding a 30 minutes pause to the beginning of the script 'works', as after the 30 minutes the subjects array is populated as the email is (99.99% of the time) sent within a 30 minute window.

However, it is definitely far from ideal as the email might be received within 15 minutes, meaning the test is 'wasting' 15 minutes.

So what I'd ideally like to do is write some form of loop (?) that tests if the subjects array is populated or not.

So if the array is populated, carry on with the rest of the test script (which entails testing that the array contains a certain text).

If the array is not populated, wait for another minute before trying again.

Continue this trying every minute until the array is populated.

I've tried setInterval, For loops, While loops, etc but I can't seem to get them to work and I'm out of ideas to be honest.

Any advice, help, references would be greatly appreciated and any more info can be promptly added if required.


Solution

  • One way to do that could be using recursion.

    const createPromise = ms => new Promise((resolve, reject) => {
          setTimeout(() => resolve(ms), ms)
        });
        
    function findUnseenEmails(connection) {
        return connection.openBox('INBOX').then(function () {
            var searchCriteria = [
                'UNSEEN'
            ];
        
            var fetchOptions = {
                 bodies: ['HEADER', 'TEXT'],
                 markSeen: false
            };
        
            return connection.search(searchCriteria, fetchOptions).then(function (results) {
                var subjects = results.map(function (res) {
                    return res.parts.filter(function (part) {
                        return part.which === 'HEADER';
                    })[0].body.subject[0];
                });
                console.log(subjects);
                return subjects.length > 0 ? subjects : createPromise(5000).then(function() { return findUnseenEmails(connection)});
          });
      });
    }
    
    imaps.connect(config).then(function (connection) {
          return findUnseenEmails(connection);
        }).then((subjects) => console.log('finished', subjects));
    

    Of course there is a possibility and danger of stack overflow, but in such scenario feel free to come back to stack overflow to find here with the help of our community non-recursive solution.

    Result:

    Gif with result

    EDIT: Answering your question regarding closing connection:
    I'd do it like this (in findUnseenEmails function)

    if (subjects.length > 0) {
        connection.end();
        return subjects;
    } else {
        return createPromise(5000).then(function() { return findUnseenEmails(connection)});
    }