4

I'm looking for a solution to define Mocha tests after getting data asynchronously.

For now, I use gulp-webdriver to getting HTML content with Selenium. And I want tests certain HTML tags structure.

For example, I want to get all buttons structure from an HTML page.

1° In Mocha Before(), I get buttons :

var buttons = browser.url("url").getHTML("button");

2° And after that, I want tests each button in a separate it :

buttons.forEach(function(button) {  it() }); 

The only solution found is loading HTML and extract buttons with Gulp before launch Mocha test with data_driven or leche.withData plugin.

Do you know another solution directly in Mocha test definition?

Thanks in advance,

ali_o_kan
  • 452
  • 5
  • 18

3 Answers3

3

It doesn't seem possible to dynamically create it() tests with mocha.

I finally organise my test like this :

it('Check if all tag have attribute', function() {
        var errors = [];
        elements.forEach(function(element, index, array) {
            var $ = cheerio.load(element);
            var tag = $(tagName);
            if (tag.length) {
                if (!tag.attr(tagAttr)) errors.push(element);
            }
        });
        expect(errors).to.be.empty;
    }
}
ali_o_kan
  • 452
  • 5
  • 18
3

You can actually create dynamic It() tests with mocha if you don't mind abusing the before() hook a bit:

before(function () {
    console.log('Let the abuse begin...');
    return promiseFn().
        then(function (testSuite) {
            describe('here are some dynamic It() tests', function () {
                testSuite.specs.forEach(function (spec) {
                    it(spec.description, function () {
                        var actualResult = runMyTest(spec);
                        assert.equal(actualResult, spec.expectedResult);
                    });
                });
            });
        });
});

it('This is a required placeholder to allow before() to work', function () {
    console.log('Mocha should not require this hack IMHO');
});
rob3c
  • 1,936
  • 1
  • 21
  • 20
  • What is promiseFn() in this context? – Amiga500 Feb 28 '17 at 11:53
  • 1
    @Wexoni `promiseFn()` represents any promise-based async operation you need to perform before dynamically generating the tests. The OP wanted to "define Mocha tests after getting data asynchronously", so returning a javascript promise in the root-level `before` callback that completes when you're async data is ready does the trick. Otherwise, you'd be limited to only syncrhronous operations. – rob3c Feb 28 '17 at 18:22
  • I am afraid I dont get it :(. I have a Main test, that calls another Helper file, and in there some other tests are generated. Main test runs, and if there are any other tests generated in helper file, they are returned and to Main, and I need to rune them as well. How can I achieve that? I made a question regarding that on SO. http://stackoverflow.com/questions/42508514/mocha-run-test-inside-the-beforeeach-hook?noredirect=1#comment72157223_42508514 – Amiga500 Mar 01 '17 at 08:13
  • C.f. the `--delay` flag for what you're trying to do instead of the fake-`it` hack: https://github.com/mochajs/mocha/issues/2221#issuecomment-214636042 – Kev Jan 15 '18 at 21:38
  • 1
    @Kev besides being a flag instead of part of your code, `--delay` forces you to pick a single entry point in your code to invoke their global `run()` function. My method let's you initialize any or all of your test files arbitrarily, and you do it in the code itself. – rob3c Jan 18 '18 at 05:42
  • @rob3c I hadn't noticed the global-ness of that. Somehow it worked out in my use case, I think because I was doing a temporary ad-hoc test using `.only()`. Good to know it wouldn't fly in real life. – Kev Jan 18 '18 at 14:03
1

Mocha supports two ways to handle asynchronicity in tests. One way is using the done callback. Mocha will try to pass a function into all of your its, befores, etc. If you accept the done callback, it's your responsibility to invoke it when your asynchronous operation has completed. Callback style:

before(function(done) { 
    browser.url("url").getHTML("button").then(function() {
        done();
    }); 
}); 

The other approach is to use Promises. Since your call to getHTML returns a Promise, you could just return that promise and Mocha would know to wait for the promise to settle before moving ahead with anything.

Here is an example of the Promise style:

before(function() { 
    return browser.url("url").getHTML("button"); 
}); 

A couple things worth noting about this: - getHtml() returns a promise for the html buttons. Whenever the asynchronous call to getHTML completes, the function passed into the then function gets invoked and the resulting value from the call to getHTML is passed in. - Returning that promise in the before lets mocha know that you're doing something asynchronous. Mocha will wait for that promise to settle before moving past your 'before'.

For your specific example, you might want to try is something like this:

describe('the buttons', function() {
   var buttons;

   before(function() { 
       return browser.url("url").getHTML("button").then(function(result) { 
            buttons = result;  
        }; 
   }); 

   it('does something', function() {
     buttons.forEach(function(button) {  
     }); 
   }); 

});
Kyle
  • 63
  • 8
  • Thank you for your complete answer, but what I want to do is used the buttons result to create `it` test. Something like that : `buttons.forEach(function(button) { it('does something', function() {}); });` – ali_o_kan Dec 01 '15 at 08:34