Has anyone found a way to add list numbering to Protractor Describe blocks?
Asked Answered
B

3

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.

Brame answered 20/10, 2014 at 9:25 Comment(0)
L
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
Lancinate answered 26/10, 2014 at 14:17 Comment(2)
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. ThanksBrame
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.Lancinate
S
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'
  ]
};
Shawannashawl answered 22/10, 2014 at 11:57 Comment(5)
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.Brame
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: screencast.com/t/0bWZAhYlUf produces such an output: 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.Juryrigged
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.Shawannashawl
The issue you presented has been addressed. You can see the change is one additional function call, an omission I spotted immediately.Shawannashawl
@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.Juryrigged
L
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
Lancinate answered 26/10, 2014 at 14:17 Comment(2)
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. ThanksBrame
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.Lancinate
J
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(){...});
Juryrigged answered 23/10, 2014 at 20:35 Comment(5)
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).Shawannashawl
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).Juryrigged
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.Shawannashawl
@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.Juryrigged
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)Shawannashawl

© 2022 - 2024 — McMap. All rights reserved.