Jasmine jquery testing of a function with no parameters - javascript

I have a following function:
function prompt_mandatory_field_completion(){
$("#mandatory_fail").show(150, function() {
setTimeout(function() {
$("#mandatory_fail").fadeOut(500)
}, 2000);
});
window.scrollTo(0,0)
}
That I would like to test with jasmine but regardless to what I put in my spec file the test seems to pass.
The spec file contains the following code :
it(' NEED TO FIX THAT FADE OUT Should prompt user to fill in mandatory questions via prompt_mandatory_field_completion function', function() {
prompt_mandatory_field_completion();
setTimeout(2000, function(){
expect($('#mandatory_fail').css('display').toEqual('random thing'));
});
In my SpecRunner.html I am using the following function that I run in before each test in this description block:
function setupFixtures(){
setFixtures('<div id="mandatory_fail" style="display:none;"></div>');
prompt_mandatory_field_completion();
};
Any idea how to make this into a meaningful test? I guess I have been staring at it way too long and poking it from all the directions.
Best,
Adam

You're trying to write a functional test of asynchronous behavior. You might have a lot better experience trying to use protractor for this sort of test. It's tuned more toward asserting things that will eventually be true.
However, jasmine does have an asynchronous facility since about 2.0, known as done(), that will insist that all of the asynchronous code has run before the test passes or fails.

You have to pass the done function to get asynchronous tests :
it(' NEED TO FIX THAT FADE OUT Should prompt user to fill in mandatory questions via prompt_mandatory_field_completion function', function(done) {
prompt_mandatory_field_completion();
setTimeout(2000, function(){
expect($('#mandatory_fail').css('display').toEqual('random thing'));
done();
});
}, 3000);
You can also pass a timeout as a last parameter, depending on what you've set in your jasmine's settings.
Otherwise, Jasmine will consider this test to fail if its execution exceed its timeout.

Related

How to pass a value from one Jasmine JavaScript test to another test

I am testing reading and writing (to a server, to a mongo db).
I know, I am not supposed to do this, I should be using mocks, ... but anyhow
I want to write a document, read that document to make sure it is was correctly written,
then delete that document, then verify it is gone. So I have 2 problems that I have solved but by using 2 hacks.
1) how do I pass along the mongo _id of the document from step to step. I'd like to have a simple variable in my Jasmine code that I can read and write from each test. I am hacking it now by creating a variable in the actual Angular module that I am testing, and reading and writing a variable over in that code.
2) since I have to wait for each IO operation before proceeding, I am taking advantage of the
setTimeout(() => {done();}, 2000); feature in a set of nested beforeEach(function(done){
sections.
I would like to learn simple, better ways of doing these if there are any.
thanks
What you're doing is called integration tests. Nothing wrong with doing them, but I usually write integration tests using Angular's E2E facilities.
That said, just save the value in a global variable and it will change each test. Some psuedo code
describe('integration test', () => {
let id;
it('should create a document', () => {
// code to create item and return id
id = _id;
}
it('should load document', () => {
console.log(id); // should be value from create test
}
it('should delete document, () => {
console.log(id); // should have value from create test
}
}
Since the id value is never set in a beforeEach() it will retain its value between tests in the same describe() block.
I have cautions about this when writing unit tests--because the tests must run in a specific order to execute. But, the desire is that E2E / integration tests are run sequentially.

Testing style set using Mocha

I'm trying to test some "style set" functions, and I'm facing issues because I need to wait until the element if fully rendered, then read its property and check if it was sucesfully changed. I know that this is pretty obvious, but I was wondering if there is a way to do the same but without waiting.
For example, I want to test this function:
function changeWidth() {
document.getElementById('test').style.width = '200px';
}
And I use this test in Mocha:
it('Width should be 200px', () => {
changeWidth();
assert.equal(document.getElementById('test').style.width, '200px');
});
That assertion will always returns false. The following test will work:
it('Width should be 200px', () => {
changeWidth();
window.setTimeout( () => {
assert.equal(document.getElementById('test').style.width, '200px');
}, 1000);
});
It has to be a better way to accomplish the same without using timeouts. Can someone guide me? Thanks!
There are two things I noted:
No, you cannot force a render to happen synchronously. The browser decides, so the test will be async and a bit messy. That being said, there are (a bit) more elegant answers than setTimeout.
Your test is synchronous, while the logic is asynchronous. That means your test will always pass, as the assertion is called after the test has finished (see this). You need to pass a callback to the test that can be called when the test has finished.
You could do the tests somewhat cleaner by using requestAnimationFrame. Just create a helper that will run each function passed to it in a seperate animation frame and you will be guaranteed separate render phases.
function runSteps(fns) {
if(!fns.length) return;
var current = fns[0];
var rest = fns.slice(1);
requestAnimationFrame(function() {
current();
runSteps(rest);
})
}
// note the `done` argument - it is what makes Mocha able to know
// when the test is finished. See https://stackoverflow.com/questions/20748918/cannot-run-mocha-js-in-synchronous-mode
it('should run dom render steps chronologically', function(done) {
function assertWidthChanged(){
assert.equal(
document.getElementById('test').style.width,
'200px'
);
}
runSteps([
changeWidth,
assertWidthChanged,
done
]);
}

Does Cucumber with Protractor need the callback?

I'm trying to understand how The WebDriver Control Flow works exactly.
According to the linked documentation (https://github.com/angular/protractor/blob/master/docs/control-flow.md) no callback method / call is needed in jasmine:
Protractor adapts Jasmine so that each spec automatically waits until the control flow is empty before exiting.
However, I have to use cucumber. I'm using the library protractor-cucumber-framework as described here: https://github.com/angular/protractor/blob/master/docs/frameworks.md#using-cucumber
It works well, but for some reason it works better when I skip the callback variable then when I try using it. For instance, this code fails:
this.Given(/^the login page is active$/, function (callback) {
browser.get('/').then(callback);
});
With the error ...
TypeError: text.split is not a function
[launcher] Process exited with error code 1
On the other hand, this codes works as I want it to work and cucumber / protractor seems to be waiting until the page is loaded, before executing further functions:
me.Given(/^the login page is active$/, function () {
browser.get('/');
});
But I couldn't find any documentation confirming that I really can omit the callback function.
Currently the page I tried to test doesn't use Angular and therefore I have the following code in my config file:
onPrepare: function() {
browser.ignoreSynchronization = true;
}
Protractor uses WebDriverJS underneath. And WebDriverJS uses a promise manager where it queues its commands. Here is some excerpts from their wiki page here
Internally, the promise manager maintains a call stack. Upon each turn
of the manager's execution loop, it will pull a task to execute from
the queue of the top-most frame. Any commands scheduled within the
callback of a previous command will be scheduled in a new frame,
ensuring they run before any tasks previously scheduled. The end
result is that if your test is written-in line, with all callbacks
defined by function literals, commands should execute in the order
they are read vertically on the screen. For example, consider the
following WebDriverJS test case:
driver.get(MY_APP_URL);
driver.getTitle().then(function(title) {
if (title === 'Login page') {
driver.findElement(webdriver.By.id('user')).sendKeys('bugs');
driver.findElement(webdriver.By.id('pw')).sendKeys('bunny');
driver.findElement(webdriver.By.id('login')).click();
}
});
driver.findElement(webdriver.By.id('userPreferences')).click();
This test case could be rewritten using !WebDriver's Java API as follows:
driver.get(MY_APP_URL);
if ("Login Page".equals(driver.getTitle())) {
driver.findElement(By.id("user")).sendKeys("bugs");
driver.findElement(By.id("pw")).sendKeys("bunny");
driver.findElement(By.id("login")).click();
}
driver.findElement(By.id("userPreferences")).click();
Now going back to your question, since you are omitting callback from your steps, cucumber is treating your test code as synchronous. See documentation here. And because the way protractor/WebdriverJS handles promise manager the way described above, everything works as expected for you.
As far as the error you are getting when using callback, I'm not sure. I do it exactly the same way you are doing. See here. I'm using cucumber ^0.9.2. It could be that your cucumber version has issues.
On a side note, I found that you could return promises instead of using callbacks to let cucumber know that you are done executing. So something like this works as well (assuming you are using ^0.9.2). I tested it,
me.Given(/^the login page is active$/, function () {
return browser.get('/');
});

Requiring timeouts when testing Meteor with Velocity and Jasmine

Pretty new to meteor, velocity and jasmine so not sure if I am doing something wrong, using Jasmine for something it's not designed for, or this is just the way it works.
I am finding I need to set timeouts for pretty much all of my tests in order to get them to pass. Should this be the case or am I doing something incorrectly?
For example some tests I am running to check validation messages:
describe("add quote validation", function() {
beforeEach(function (done) {
Router.go('addQuote');
Tracker.afterFlush(function(){
done();
});
});
beforeEach(waitForRouter);
it("should show validation when Quote is missing", function(done) {
$('#quote').val('');
$('#author').val('Some author');
Meteor.setTimeout(function(){
$('#addQuoteBtn').click();
}, 500);
Meteor.setTimeout(function(){
expect($('.parsley-custom-error-message').text()).toEqual("Quote can't be empty.");
done();
}, 500);
});
}
OK, we've had this exact same problem and devised a pretty elegant solution to it, that doesn't require timeouts and is the fastest way to run your tests. Basically, we use one of two strategies, depending on what screen elements you're waiting for.
All code goes into tests/mocha/client/lib.coffee, not 100% what the Jasmine equivalent is, but it should be available to all client test code. I've left it in Coffeescript, but you can compile it on coffeescript.org into Javascript, it should work fine as well.
If whatever you do (routing or something else like changing a reactive variable) causes a Template to (re)render, you can use the Template.<your_template>.rendered hook to detect when it is finished rendering. So, we've added the following function in lib.coffee:
#afterRendered = (template,f)->
cb = template.rendered
template.rendered = ->
cb?()
template.rendered = cb
f?()
return
return
What does it do? It basically "remembers" the original rendered callback and temporarily replaces it with one that calls an extra function after the template is rendered and the original callback is called. It needs to do this sort of housekeeping to avoid breaking any code that may have depended on the rendered callback, as you're basically messing with the Meteor code directly.
In your test, you can then do something like this:
it.only "should check stuff after routing", (done)->
try
Router.go "<somewhere>"
afterRendered Template.<expected_template>, ->
<your tests here>
done()
catch e
done(e)
I'd recommend the try-catch as well, as I've noticed asynchronous errors don't always make it into the velocity system, merely giving you a timeout failure.
OK, then there are things that don't actually re-render, but are generated with JS or by some kind of "show/hide" mechanism. For that, you do need some kind of timeout, but you can reduce the "time cost" of the timeout by using a polling mechanism.
# evaluates if a JQuery element is visible or not
$.fn.visible = -> this.length > 0 and this.css('display') isnt 'none'
# This superduper JQuery helper function will trigger a function when an element becomes visible (display != none). If the element is already visible, it triggers immediately.
$.fn.onVisible = (fn,it)->
sel = this.selector
if this.visible()
console.log "Found immediately"
fn?(this)
else
counter = 0
timer = setInterval ->
counter++
el = $(sel)
if el.visible()
fn?(el)
clearInterval timer
console.log "Found on iteration #{counter}"
else
it?(el)
, 50
You can remove the console logging and secondary it iterator function if you like, they're not important. This allows you to do something like this in your test:
$('#modalId').onVisible (el)->
<tests here>
done()
, (el)->
console.log "Waiting for #{el.selector}"
You can remove the second function if you want, it is the it iterator function mentioned above. However, do note that this particular code works with "display: hidden" as the marker for invisibility (Bootstrap does this). Change it if your code uses another mechanism to hide/show parts.
Works like a charm for us!

Open RequireJS Application with PhantomJS

I'm trying to load a single page application that uses a heavy amount of async code execution involving RequireJS and jQuery deferreds. The application loads as expected inside the browser, but not in PhantomJS.
For instance, I spent some time trying to figure out how to make the following snippet work:
# index.html
<body>
<script>
require.config({
base: '.',
paths: {
main: 'main'
}
})
require(['main'], function() {
window.myglobal = {
something: 'foo'
}
});
</script>
</body>
# phantomjs
page.evaluateAsync(function() {
console.log(window.myglobal.something); // Should print out 'foo'.
}, 100);
I consider that using evaluateAsync with a fixed timeout that has to be determined by trial and error is not really satisfactory. Perhaps someone can suggest a better pattern.
The documentation for evaluateAsync does not say much so I'm going to take it at face value and assume that it just executes the code asynchronously, without any further constraint regarding what may or may not have loaded already. (The source code does not indicate any further constraints either.)
The problem I'm seeing is that you have two asynchronous functions that may execute in any order. When require(['main'], ...) is called, this tells RequireJS to start loading the module but you don't know when the module will actually load. Similarly, when you execute page.evaluateAsync you are telling PhantomJS to execute a piece of code asynchronously. So it will execute but you don't know when.
So the following can happen:
The module finishes loading: window.myglobal is set.
console.log is called, which outputs the correct value.
Or:
console.log is called, which fails.
The module finishes loading: window.myglobal is set.
Setting a timeout that delays the execution of console.log will make it more likely that the first case happens but it does not guarantee it.
What you could do is change your HTML like this:
<body>
<script>
require.config({
base: '.',
paths: {
main: 'main'
}
})
define('init', ['main'], function () {
window.myglobal = {
something: 'foo'
};
});
require(['init']);
</script>
</body>
Your PhantomJS script:
page.evaluateAsync(function() {
require(['init'], function () {
console.log(window.myglobal.something); // Should print out 'foo'.
});
});
What this does is define a module called init. (This is a rare case where explicitly naming your module with define is okay. Usually you just start the define call with the list of dependencies.) Then when evaluateAsync is called it asks for the init module, which guarantees that the assignment to window.myglobal will have happened before console.log runs.
It would also be possible to use promises to get the desired results but I've preferred to show a solution that uses only RequireJS.
PhantomJS is a headless browser that is used for all kinds of stuff. A big part of it is the testing/automation of websites. It means that you generally don't have the opportunity of changing the site code. Most of the time it is not necessary, such as in this case.
You simply need to wait until the page script/DOM is at a state that you want for further processing. This is usually done using waitFor from the examples of PhantomJS.
In your case, you can add the waitFor definition to the beginning of the script and wait for window.myglobal to be defined:
page.open(url, function(){
waitFor(function check(){
return page.evaluate(function(){
return !!window.myglobal;
});
}, function then(){
// do something useful
}, 10000); // upper bound on acceptable wait timeout
});
check is a function which is called periodically to check that a certain condition is met. So the logic is that as soon as the condition is met, you can do something useful including doing something on the page using page.evaluate from the then callback.
There are also ways not to wait for specific variables/DOM nodes, but waiting for general ending of network activity as in this answer.

Categories