6

I have a very large number of Describe blocks in my Protractor tests. I get pages and pages of test output all correctly indented but it's difficult to see which test is which and how far the tests have progressed.

Has anyone tried to add a list numbering to the Describe. Something like this:

1.   Main Page test
   1.1  Test xxx
   1.2  Test yyy
      1.2.1 Describe in describe in describe test
2.   XXX Page test
   2.1  Test abc

Note that here the first and maybe second number after dots would be a result of describes with describes.

Bastien Caudan
  • 4,482
  • 1
  • 17
  • 29
Samantha J T Star
  • 30,952
  • 84
  • 245
  • 427

3 Answers3

3

You can write a (not so) simple "plugin" that adds this functionality. I'd avoid replacing the original describe and it functions. The only downside of my approach is that you'll have to do a search and replace from describe and it to lp.describe and lp.it respectively.

(Yes, you could just overwrite the original describe and it if you are sure it won't affect anything else - and it should not, but just to be on the safe side, don't :) )

My approach, updated to take into account the fact that you can have a describe inside another describe:

list-plugin.js

(function(protractorDescribe, protractorIt) {
    var level = -1;
    var ids = [1];

    function levelLabel() {
        var label = ids.join('.');
        if(ids.length === 1) {
            label += '.';
        }
        return label;
    }

    function startDescribe() {
        startIt();
        level += 1;
    }

    function endDescribe() {
        ids[level] += 1;
        ids.pop();
        level -= 1;
    }

    function startIt() {
        if(!ids[level + 1]) {
            ids[level + 1] = 1;
        }
    }

    function endIt() {
        ids[level + 1] += 1;
    }

    function describe(name, body) {
        var protractorResult;

        startDescribe();
        protractorResult = protractorDescribe(levelLabel() + ' ' + name, body);
        endDescribe();

        return protractorResult;
    }

    function it(name, body) {
        var protractorResult;

        startIt();
        protractorResult = protractorIt(levelLabel() + ' ' + name, body);
        endIt();

        return protractorResult;
    }

    exports.describe = describe;
    exports.it = it;

})(describe, it);

spec.js

var lp = require('./list-plugin.js');

lp.describe('Main Page test', function() {
    lp.it('Test xxx', function() {
        expect('a').toEqual('a');
    });

    lp.describe('Test yyy', function() {
        lp.it('Describe in describe test', function() {
            expect('a').toEqual('a');
        });
    });
});

lp.describe('XXX Page test', function() {
    lp.it('Test abc', function() {
        expect('a').toEqual('a');
    });
});

conf.js

exports.config = {
  seleniumAddress: 'http://localhost:4444/wd/hub',
  jasmineNodeOpts: {
    isVerbose: true
  },
  specs: [
    'spec.js'
  ]
};
Sergiu Paraschiv
  • 9,929
  • 5
  • 36
  • 47
  • I think this is a good start. How could I extend this so that it gives multi-level numbering as in my question example where the second and third levels were a result of describe within describe etc. – Samantha J T Star Oct 23 '14 at 04:51
  • There are two issues with your answer: `1.` it requires to use custom `lp.describe` `2.` it does not seem to work correctly -- this *simple* spec: http://screencast.com/t/0bWZAhYlUf produces such an output: http://screencast.com/t/qpdwpzfC (check it yourself). `1.` may be overcome rather easily, but changing your current approach into a working solution does not seem to me as a trivial task. – artur grzesiak Oct 23 '14 at 20:41
  • Actually, modifying protractor source code should be a deal breaker for anyone. If you are testing your code you most probably also use some sort of dependency manager that automatically keeps all your libraries up to date. Secondly, as I clearly state and anyone with minimal JavaScript understanding can implement, it would be trivial to overwrite the original functions themselves. – Sergiu Paraschiv Oct 23 '14 at 21:24
  • The issue you presented has been addressed. You can see the change is one additional function call, an omission I spotted immediately. – Sergiu Paraschiv Oct 23 '14 at 22:03
  • @SergiuParaschiv indeed -- it seems to be addressed. To tell the truth I thought yesterday it would not work, therefore wanted to invented some hack. For sure your approach is **way better** than using `Error.stack`. +1. – artur grzesiak Oct 24 '14 at 08:16
3

You can use jasmine spec reporter (>= 1.1.0) with the displaySuiteNumber option to display the output of your protractor tests.

Output example:

1 first suite
  ✗ should failed
    - Expected true to be false.
  ✓ should be ok

2 second suite
  ✗ should failed
    - Expected true to be false.
  ✓ should be ok

  2.1 first child suite

    2.1.1 first grandchild suite
      ✗ should failed
        - Expected true to be false.
        - Expected true to be false.
      ✗ should failed
        - Expected true to be false.
      ✓ should be ok
Bastien Caudan
  • 4,482
  • 1
  • 17
  • 29
  • Thanks for your suggestion. Out of interest do you think this is a feature that could be added directly to the Jasmine Spec Reporter? I like the idea of the custom processors but it would maybe be easier going forward if these were available as config features. Thanks – Samantha J T Star Oct 27 '14 at 15:03
  • 1
    I try to limit the amount of config options, custom processors were introduced for that reason. But you are right, this option could be useful for other users. I'm gonna add it as a config feature. – Bastien Caudan Oct 27 '14 at 21:38
2

Probably the most elegant solution would be to change protractor's code. But it might be problematic if needed to upgrade the library.

I came up with a working solution by decorating protractor's describe instead. The only caveat is that it requires the spec code to be indented correctly. Actually this limitation may be considered as a feature, as for sure it is a good practice to have the code indented correctly and with current IDEs it is just a 2-sec-task. You can reset the counter (e.g. at the beginning of each spec) by calling require('./protractor-decorator').resetCounter();.

UPDATE

If you want to decorate the it just call it = require('./protractor-decorator.js').decorateUsingErrorStack(it); or refactor it into a single method.

protractor-decorator.js module:

var stack = [];
var lastIndentColumn = 1;

function decorateUsingErrorStack(origDescribe){

    function describe(){
        var callerIndent, args;

        if(stack.length === 0){
            stack.push(0);
        }

        // from current stack we get the information about indentation of the code
        callerIndent = new Error().stack.split('\n')[2].split(':');
        callerIndent = parseInt(callerIndent[callerIndent.length-1]);

        if(callerIndent == lastIndentColumn){
            stack[stack.length-1] += 1;
        }
        else {
            if(callerIndent < lastIndentColumn){
                stack.pop();
                stack[stack.length-1] += 1;
            }
            else {
                stack.push(1);
            }
        }
        lastIndentColumn = callerIndent;

        args = Array.prototype.slice.call(arguments, 0);

        origDescribe.call(null, stack.join('.') + '.   ' + args[0], args[1]);
    }

    return describe;
}


module.exports = {
    decorateUsingErrorStack : decorateUsingErrorStack,
    resetCounter : function(){
        // this function should be called to start counting from 1.
        stack = [];
        lastIndentColumn = 1;
    }
} 

spec.js file:

describe = require('./protractor-decorator.js').decorateUsingErrorStack(describe);

describe(' should be 1.', function(){

    describe('should be 1.1.', function(){
        it('xxx', function(){

        });

        describe('should be 1.1.1.', function(){
            it('xxx', function(){

            });

            describe('should be 1.1.1.1', function(){
                it('xxx', function(){

                });
            });

            describe('should be 1.1.1.2', function(){
                it('xxx', function(){

                });
            });

        });

        describe('should be 1.1.2.', function(){
            it('xxx', function(){

            });
        });

    });

    describe('should be 1.2.', function(){
        it('xxx', function(){

        });
    });

    describe('should be 1.3.', function(){
        it('xxx', function(){

        });
    });

});

// same as above but all starts with 2.
describe(' should be 2.', function(){...});
artur grzesiak
  • 20,230
  • 5
  • 46
  • 56
  • 1. you are not handling 'it' descriptions. 2. if I wrap the 'describe' calls in another function the indentation will be off by one, and I always 'use strict' and wrap inside an anonymous function. 3. Error().stack has a different implementation in Firefox. This could cause issues. 4. I'd set 'Error.stackTraceLimit' to 1 to reduce the performance impact this approach obviously has (getting a stack trace involves stopping code execution). – Sergiu Paraschiv Oct 23 '14 at 21:37
  • 1. OP asked for `describe` 2. it is a seed of a solution and starting indentation is not a problem as it can be changed easily 3. ??? in Firefox ??? could you elaborate (these specs are not going to run in FF anyway) 4. I'd rather not care about performance at this stage (as benefit would be pretty marginal). – artur grzesiak Oct 23 '14 at 21:51
  • no, OP asked for a specific output that your solution does not produce. My solution was a seed too, but seeing that you are interested in a complete one I expanded it to cover additional scenarios. OP did not specify under what environments she runs the tests, but if she's doing e2e testing like I do on a regular basis then she most likely does it in a lot of different browsers. When you run that many tests that it becomes hard to follow the output you most certainly don't block the whole application while it's running AJAX calls and all the other e2e stuff happening. – Sergiu Paraschiv Oct 23 '14 at 22:12
  • @SergiuParaschiv so to quote OP: *Has anyone tried to add a list numbering to the Describe*. Specs do run on server-side and communicate with browser via webdriver, so point about FF is completely missed. – artur grzesiak Oct 24 '14 at 07:55
  • OK, I retract point (3), you are right, `Error().stack` calls are not executed in the browser against which you are testing. I still think relying on code formatting is wrong and is the single most important drawback of your solution. Both our solutions are decent (with the help of outside eyes it always gets better) with minor drawbacks and I'm not here to discredit your solution. (+1 for coming up with the error stack idea) – Sergiu Paraschiv Oct 24 '14 at 08:22