1

I am trying to iterate through the JSON files generated by the protractor tests. I pull all the file names into an array and call a method that opens and parses through the each file, post the results to the database and pass back a passed/failed flag.

I have tried all the examples here Make angular.forEach wait for promise after going to next object and still get the same results.

The method is actually called, but the results are not posted to the db. I have tested the parser.parseResults on an individual file and it successfully posted to the db, so it has to have something to do with the promise not resolving correctly.

Is it not possible to do something like this in the jasmine/protractor framework? Or do I have something wrong in the code?

I have included the code for my latest attempt. Thank You Christine

matches.reduce(function (p, val) {
    console.log('val', val);
    return p.then(function () {
          return parser.parseResults(val);
     });
   }, Promise.resolve()).then(function (finalResult) {
       console.log('finalResult = ', finalResult);
   }, function (err) {
       console.log('error in reduce',err);
   });

parser.parseResults code

protractorParser.prototype.parseResults = function (fileName) {
    return new Promise((resolve, reject) => {

        console.log('In parseresults', fileName);
        json.readFile(fileName, function (err, obj) {
            try {

                if (err != null) {
                    console.log('error reading file',err);
                    reject(err);
                }
                console.log('obj - ',obj);
                var results = [];

                var Passed = 0;
                var Message = '';
                var Stack = '';
                for (var suite in obj) {
                    var specs = obj[suite].specs;
                    console.log('spec - ', specs);
                    if (specs.length > 0) {
                        for (var i = 0; i < specs.length; i++) {
                            var assert = specs[i];
                            var tcR = new RegExp(/TC[\d]+/);
                            var tc = assert.description.match(tcR);

                            if (!assert.failedExpectations.length) {
                                Passed = 1;
                            }
                            else {
                                assert.failedExpectations.forEach((expectation) => {
                                    Message = expectation.message;
                                    Stack = expectation.stack.split('\n')[1].trim();
                                })
                                Passed = 0;
                            }
                            if (tc != null) {
                                utility.TestDataManager.insertAutomationResults(tc[0], assert.description, Passed, process.env.testBuild,
                                    'P', Message, Stack, 0, moment().utcOffset(config.get('settings.timeOffset')).format('YYYY-MM-DDTHH:mm:ss'), '')
                                    .then(function (resp) {
                                        resolve(Passed);

                                    }, (err) => {
                                        console.log('Posting to Database failed ', err);
                                        reject(err);
                                    });
                            } else {
                                console.log('no test case found for test: ' + assert.description + ' -- skipping');
                                reject(err); 
                            }
                        }
                    }
                }
            }
            catch (err) {
                console.log('rejecting opening file');
                reject(err);
            }
        });
    })
}
  • Please also post the code of `parser.parseResults` (and anything important it uses). The snippet you posted now appears to be fine. – Bergi Feb 13 '18 at 23:25

1 Answers1

1

If there is not exactly one suite in the obj, with exactly one spec, then your promise is either resolved not at all or multiple times.

Avoid wrapping too many things in the new Promise constructor - always promisify on the smallest possible level, and use promise chaining afterwards.

protractorParser.prototype.parseResults = function (fileName) {
    return new Promise((resolve, reject) => {
        console.log('In parseresults', fileName);
        json.readFile(fileName, function (err, obj) {
            if (err != null) {
                console.log('error reading file', err);
                reject(err);
            } else {
                resolve(obj);
            }
        });
    }).then(function(obj) {
        console.log('obj - ',obj);
        var results = [];

        for (var suite in obj) {
            var specs = obj[suite].specs;
            console.log('spec - ', specs);
            for (let i = 0; i < specs.length; i++) {
                const assert = specs[i];
                const tcR = /TC[\d]+/;
                const tc = assert.description.match(tcR);

                let Passed = 1;
                let Message = '';
                let Stack = '';
                if (assert.failedExpectations.length) {
                    const expectation = assert.failedExpectations[assert.failedExpectations.length-1];
                    Passed = 0;
                    Message = expectation.message;
                    Stack = expectation.stack.split('\n')[1].trim();
                }
                if (tc != null) {
                    const time = moment().utcOffset(config.get('settings.timeOffset')).format('YYYY-MM-DDTHH:mm:ss');
                    const promise = utility.TestDataManager.insertAutomationResults(tc[0], assert.description, Passed, process.env.testBuild, 'P', Message, Stack, 0, time, '');
                    results.push(promise.catch(err => {
                        console.log('Posting to Database failed ', err);
                        throw err;
                    }));
                } else {
                    console.log('no test case found for test: ' + assert.description + ' -- skipping');
                    // I don't think you want to `throw err` here, right?
                }
            }
        }
        return Promise.all(results);
    });
};
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • HI, Thank you so much for the response. Obviously, I am VERY new to javascript programming and I inherited the code from someone else....soooo. Trying to figure it all out.There actually is just 1 suite and 1 spec in each file. There used to be multiple but things changed. – Christine Edwards Feb 14 '18 at 12:42
  • So I would use the matches.reduce code above to call the parseResults method correct? – Christine Edwards Feb 14 '18 at 12:48
  • I have one more question. If the insert into the database was successful I need to delete the file and add 1 to a insertpassed count. Is there an easy way to do that in here results..push(promise.catch(err => { console.log('Posting to Database failed ', err); throw err; – Christine Edwards Feb 14 '18 at 15:55
  • First of all, write fault-tolerant code that is prepared for the case that there is not exactly 1 suite with 1 spec per file. (This is the only case I figured where your original code would have failed and hanged without an error). So what should `insertPassed` count, the processed files or the calls to `insertAutomationResult`? It might not always be the same number. Put it in the appropriate location. – Bergi Feb 14 '18 at 16:10
  • Regarding the deletion of the file, you should do it after the `Promise.all` (i.e. all database insertions ok) not inside the loop. You would basically just put a `.then(() => { return json.promiseDeleteFile(fileName); })` or `.then(() => { return new Promise((resolve, reject) => { json.deleteFile(fileName, err => { if (err) reject(err) else resolve(); }); });})` at the end of the promise chain. (I.e. either after the `Promise.all(results)`, or after the `.then(function(obj){…})`, [it does not make a difference](https://stackoverflow.com/a/22000931/1048572)). – Bergi Feb 14 '18 at 16:10
  • InsertPassed - The number of successful records inserted into the database. I also need a testPassedCount and testFailedCount. I actually have all of those working. What I am continuing to have a problem with is deleting the files and also passing back a result set that has those counts to the calling method. If I don't add the delete, I get the results. If I add the results, the return comes back undefined – Christine Edwards Feb 14 '18 at 18:00
  • In that case, you probably would use `results.push(promise.then(() => InsertPassed++; }, err => { console.log('… failed', err); throw err; }));` – Bergi Feb 14 '18 at 18:02
  • HI, I was actually wrong the insertPassed for some reason is being reinitialized to 0 when it is inside the results.push. I have defined this var testResults = { insertSuccess: 0, insertFailed: 0, testPassedCount: 0, testFailedCount: 0 } and am incrementing the testResults.insertSuccess in the results.push, but when I output the testResults data, the InsertSuccess is 0, but the testPassedCount is still = to 2 which is the number of files I am processing – Christine Edwards Feb 14 '18 at 19:31
  • You probably should [ask a new question](https://stackoverflow.com/questions/ask) and include that code there – Bergi Feb 14 '18 at 19:40
  • I understand and I will post another question. Thank you Very Much for all your help. Do you have a suggestion on where I could get some good detailed information on how promises work? – Christine Edwards Feb 14 '18 at 20:42