0

I'am using the following mailListner library for an E2E Protractor test and also based on the info from this posting which looks quite good.

The issue I'am facing is regarding the function getLastEmail():

import { mailListener } from 'mail-listener2';

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

    mailListener.on("mail", function(mail){
        deferred.fulfill(mail);
    });
    return deferred.promise;
}

When I run the test, I keep getting the error: - Failed: Cannot read property 'on' of undefined

It looks like the mailListner is undefined.

Here is the part where the function is being invoked:

describe('sales App', () => {
   it('Should send confirmation email', () => {

      browser.controlFlow().wait(getLastEmail())
            .then((email) => {
                expect(email['subject']).toEqual("Confirm Registration");
                expect(email['headers'].to).toEqual("firstName@yyy.com");
                const pattern = /Registration code is: (\w+)/g;
                const regCode = pattern.exec(email['text'])[1];
                console.log(regCode);

            });
   });
});

protractor.confi.js:

onPrepare() {
    require('ts-node').register({
        project: 'e2e/tsconfig.e2e.json'
    });
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));

    var MailListener = require("mail-listener2");
    // here goes your email connection configuration
    var mailListener = new MailListener({
        username: "myEmail@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.on("error", function(err) {
        console.log(err)
    })

    mailListener.on("server:disconnected", function() {
        console.log("imapDisconnected")
    })

    global.mailListener = mailListener


},
onCleanUp: function() {
    mailListener.stop()
}

Any idea how to work around and fix it?

k.vincent
  • 3,743
  • 8
  • 37
  • 74

1 Answers1

1

I'm guessing its referencing this line, mailListener.on("mail"..., so your mailListener is undefined.

If you originally followed this answer and are instantiating mailListener in your config, you'll notice they create a global for mailListener which is available to the function getLastEmail when it's called.

Or if you are doing it in your specs (i.e. in a beforeAll block), you would still create a variable to reference mailListener when you instantiate it, and then pass it to the function.

// page objects
getLastEmail = function (serv) {
    var deferred = protractor.promise.defer();
    console.log("Waiting for an email...");

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

// specs
const MailListener = require("mail-listener2");
mailListener = new MailListener({...
browser.wait(Util.getLastEmail(mailListener), 30000).then(function (mail) {...
Gunderson
  • 3,263
  • 2
  • 16
  • 34
  • 1
    Yes, I do have the mailListener defined in the `protractor.conf.js` within the function: `onPrepare()`, and in `spec.ts` I do have the function `getLastEmail ()` at the top before `describe(....)`. Is it then not available to the `spec.ts` file? Somehow it's not available! – k.vincent Feb 01 '18 at 12:55
  • 1
    Please post the `onPrepare` block where you are instantiating mailListener – Gunderson Feb 01 '18 at 13:06
  • I have added the `onPrepare()` block from `protractor.conf.js` – k.vincent Feb 01 '18 at 13:13
  • 1
    Ok thanks, looks good there. Also are you using TypeScript? Maybe it's missing that global reference when compiling? You could also try moving mailListener to the spec level and instantiate it there, then it will definitely be available – Gunderson Feb 01 '18 at 14:00
  • Yes, TypeScript - it's an angular app. I checked this posting regarding global variables and passing them from `protractor.conf.js` to `spec` file: [link](https://stackoverflow.com/questions/31203398/protractor-set-global-variables), but it didn't help. And I also tried to import `mailListener` directly into `spec` like `import { mailListener } from '../../protractor.conf.js';` but i says: `/protractor.conf.js', but '--allowJs' is not set` which is clear what it means. – k.vincent Feb 01 '18 at 14:06
  • Ok, if add it directly into `spec` it seems works, but then it stops by the line: `Waiting for an email...` and then it throws the following: `A Jasmine spec timed out. Resetting the WebDriver Control Flow.` and `- Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.`. Any idea? – k.vincent Feb 01 '18 at 14:32
  • Whats your `defaultTimeoutInterval`? Did you receive an email within the timeout period? – Gunderson Feb 01 '18 at 14:36
  • Here is my `defaultTimeoutIntervaL` as in `conf.js`: `jasmineNodeOpts: { showColors: true, defaultTimeoutInterval: 30000, print: function() {} },` And I didn't receive no email regarding timeout. – k.vincent Feb 01 '18 at 14:45