Build Mocha test dynamically after getting data from webdriver.io - javascript

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,

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;
}
}

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');
});

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) {
});
});
});

Related

Wait for an own function (which returns a promise) before tests are executed

I'm new to cypress and am trying to figure out how things work.
I have my own function (which calls a test controller server to reset the database). It returns a promise which completes when the DB have been successfully resetted.
function resetDatabase(){
// returns a promise for my REST api call.
}
My goal is to be able to execute it before all tests.
describe('Account test suite', function () {
// how can I call resetDb here and wait for the result
// before the tests below are invoked?
it('can log in', function () {
cy.visit(Cypress.config().testServerUrl + '/Account/Login/')
cy.get('[name="UserName"]').type("admin");
cy.get('[name="Password"]').type("123456");
cy.get('#login-button').click();
});
// .. and more test
})
How can I do that in cypress?
Update
I've tried
before(() => {
return resetDb(Cypress.config().apiServerUrl);
});
But then I get an warning saying:
Cypress detected that you returned a promise in a test, but also invoked one or more cy commands inside of that promise
I'm not invoking cy in resetDb().
Cypress have promises (Cypress.Promise), but they are not real promises, more like duck typing. In fact, Cypress isn't 100% compatible with real promises, they might, or might not, work.
Think of Cypress.Promise as a Task or an Action. They are executed sequentially with all other cypress commands.
To get your function into the Cypress pipeline you can use custom commands. The documentation doesn't state it, but you can return a Cypress.Promise from them.
Cypress.Commands.add('resetDb', function () {
var apiServerUrl = Cypress.config().apiServerUrl;
return new Cypress.Promise((resolve, reject) => {
httpRequest('PUT', apiServerUrl + "/api/test/reset/")
.then(function (data) {
resolve();
})
.catch(function (err) {
reject(err);
});
});
});
That command can then be executed from the test itself, or as in my case from before().
describe('Account', function () {
before(() => {
cy.resetDb();
});
it('can login', function () {
// test code
});
})
You can use cy.wrap( promise ), although there might still be a bug where it never times out (haven't tested).
Otherwise, you can use cy.then() (which is undocumented, can break in the future, and I'm def not doing any favors by promoting internal APIs):
cy.then(() => {
return myAsyncFunction();
});
You can use both of these commands at the top-level of spec like you'd use any command and it'll be enqueued into cypress command queue and executed in order.
But unlike cy.wrap (IIRC), cy.then() supports passing a callback, which means you can execute your async function at the time of the cy command being executed, not at the start of the spec (because expressions passed to cy commands evaluate immediately) --- that's what I'm doing in the example above.

promise & mocha: done() in before or not?

I am reading some tutorials on promise tests in mocha. There is a piece of codes:
before(function(done) {
return Promise.resolve(save(article)).then(function() {
done();
});
});
Why done() called in the then() in the before()? What is the difference between the above codes and the following codes:
before(function(done) {
return Promise.resolve(save(article));
});
Thanks
UPDATE
My question is to compare with the following codes:
before(function() {
return Promise.resolve(save(article));
});
Sorry for the typo.
The first code snippet with the before hook returns a promise and calls done. In Mocha 3.x and over, it will result in this error:
Error: Resolution method is overspecified. Specify a callback *or* return a Promise; not both.
It used to be that it did not particularly matter if you used done and returned a promise, but eventually the Mocha devs figured that specifying both done and returning a promise just meant the test designer made a mistake and it was better to have Mocha pitch a fit rather than silently allow it.
In your 2nd snippet, you have the done argument and return a promise but Mocha will still wait for done to be called and will timeout. (It really should detect the argument and raise an error like in the 1st case, but it doesn't...)
Generally, if you are testing an asynchronous operation that produces a promise, it is simpler to return the promise than use done. Here's an example illustrating the problem:
const assert = require("assert");
// This will result in a test timeout rather than give a nice error
// message.
it("something not tested properly", (done) => {
Promise.resolve(1).then((x) => {
assert.equal(x, 2);
done();
});
});
// Same test as before, but fixed to give you a good error message
// about expecting a value of 2. But look at the code you have to
// write to get Mocha to give you a nice error message.
it("something tested properly", (done) => {
Promise.resolve(1).then((x) => {
assert.equal(x, 2);
done();
}).catch(done);
});
// If you just return the promise, you can avoid having to pepper your
// code with catch closes and calls to done.
it("something tested properly but much simpler", () => {
return Promise.resolve(1).then((x) => {
assert.equal(x, 2);
});
});
With regards to the completion of asynchronous operations, it works the same whether you are using it, before, beforeEach, after or afterEach so even though the example I gave is with it, the same applies to all the hooks.
I am not sure if I understood 100% the question, but the tests will not start until done is called.
beforeEach(function(done) {
setTimeout(function() {
value = 0;
done();
}, 1);
});
This test will not start until the done function is called in the call to beforeEach above. And this spec will not complete until its done is called.
it("should support async execution of test preparation and expectations", function(done) {
value++;
expect(value).toBeGreaterThan(0);
done();
});
You don't have to pass done in your example, just:
before(function() {
return Promise.resolve(save(article));
});
If you do pass done the test runner will expect to be called before continue, otherwise it will probably throw a timeout error.
In this particular case there is no functional difference. The callback, often called done, was introduced to handle asynchronous tests when using callbacks. Returning a Promise is sufficient, but note that you cannot define the done callback, because the test suite will wait until it's called. Use done when you can't easily return a Promise. In your case the second test will be infinite, because you define done, which you never actually call.

Jasmine-node assertions within promises not shown

The below code all functions correctly with the exception of the assertion. When the test is run Jasmine reports 0 assertions. How can I keep my assertions within the promises and have them be recognized?
it("should open save NTP server modal", function (done) {
var addModal = driver.findElement(By.className('modal-dialog'));
driver.wait(until.stalenessOf(addModal), 5000).then(function () {
return driver.wait(until.elementIsEnabled(driver.findElement(By.id('saveButton'))), 5000).then(function (element){
return element.click();
});
});
driver.findElement(By.className("modal-body")).then(function (element) {
return expect(element.isDisplayed()).toBeTruthy();
});
done();
});
I know that in this specific case(my best example test) I could just catch the element and then do the expect outside of a promise:
var element = driver.findElement(By.className("modal-body"));
expect(element.isDisplayed()).toBeTruthy();
Unfortunately I have other cases where I cannot determine a way to do the exception outside of a promise.
You have to put your "done" method inside the final callback.
driver.findElement(By.className("modal-body")).then(function (element) {
expect(element.isDisplayed()).toBeTruthy();
done();
});
You can also look into Chai promises library, which is meant for handling async expects.

Jasmine-Jquery - return different async content for different calls to spyOn

I am trying to return different values on multiple sypOn calls in Jasmine 1.4.1. I have the following code based on the answer here:
beforeEach(function (done) {
loadFixtures('fixture.html');
spyOn(dynamicContent, 'get').and.returnValues($ver1.getHtml(done),
$ver2.getHtml(done));
});
EDIT
I realise the above is for Jasmine 2.4 and above.
I also tried the other answers for older version but I get the same issue.
var alreadyCalled = false;
spyOn(dynamicContent, "get").and.callFake(function() {
if (alreadyCalled) return $ver1.getHtml(done);
alreadyCalled = true;
return $ver2.getHtml(done);
});
I should point out that I'm using jasmine-jquery.
My getHtml functions look like this and are identical for test purposes:
function getHtml(done) {
var deferred = $.Deferred();
$.get('some.html').then(function (data) {
// will only make it this far for the first call to getHtml()
deferred.resolve(data);
done();
});
return deferred.promise();
}
The problem is that only the first call to spyOn succeeds.
I'm wondering if it is due to the way I pass done to the function and resolve it within getHtml()
How can I make sure both async calls get returned on subsequent calls to the spy?

How do async tests work in intern?

How do asynchronous tests work in Intern testing framework? I have tried to get them run exactly as in the example, but the async test passes immediately without waiting for the callback to be run.
it('should connect in 5 seconds', function () {
var dfd = this.async(5000);
conn.connect(credentials, dfd.callback(function(result) {
expect(result).to.... something
}));
}
The test passes immediately. What am I doing wrong?
dfd.callback doesn’t execute anything until it itself is executed. Keep in mind that it is designed for promise callbacks (i.e. the function passed to promise.then), not Node.js-style callbacks where the argument might be an error (i.e. function (error, result) {}). It will not check to see if an error is passed as an argument.
Without knowing what conn is, but seeing how you are passing dfd.callback as an argument to something that is not a promise, my suspicion is you are trying to use a Node.js-style callback and the call is erroring immediately. We may provide a convenience wrapper for these types of callbacks in the future to convert them to a promise interface, but until then, you probably just need to do something like this:
it('should connect in 5 seconds', function () {
var dfd = this.async(5000);
conn.connect(credentials, dfd.callback(function(error, result) {
if (error) {
throw error;
}
expect(result).to.... something
}));
});
Otherwise, without knowing what conn is and seeing what your actual assertion is, it’s too hard to say what the issue is here. So long as nothing inside the callback throws an error, the test will be considered successful.
Edit: So based on your comments above it sounds like your callback is an event listener called multiple times with different information. In this case, what you could do is something like this:
it('should connect in 5 seconds', function () {
var dfd = this.async(5000);
conn.connect(credentials, dfd.rejectOnError(function (result) {
if (result !== 'what I want') {
return;
}
expect(result).to.... something
// all other tests…
// nothing threw an error, so it is a successful test
dfd.resolve();
}));
});
dfd.rejectOnError works just like dfd.callback except it does not automatically resolve the promise; you do that yourself at the end.
Your structure is okay. dfd sets a timeout of 5 seconds for the test to succeed, then immediately tries conn.connect(), which is not part of the intern framework. If you are trying a simple XHR request, try the getUrlCallback function instead.
They have a pretty cool list of tests at this url: https://github.com/theintern/intern/wiki/Writing-Tests . Look for the two async examples.

Categories

Resources