I am doing test-driven development with Qunit: when creating a new function, I write tests for it, create the function, reload the page, and if all the tests pass I move on... Whilst this works fine at the beginning, it starts to become a time consuming process after a while as all the tests take several seconds to run, and that's the time I have to wait for every time I refresh my browser.
In an attempt to workaround that problem, I thought about introducing Zombie.js to perform head-less testing: the idea is to have Zombie.js continuously check the webpage (e.g. $ watch -n1 "node queryTheWebpage.js") and report to me Qunits's results while coding (once in a while, as Zombie.js isn't a "real" browser, I would open up a browser and check the page manually to validate).
So far here is what I have for my node/Zombie piece of code:
browser.visit("http://localhost/mywebpage.html", function () {
var qunit_tests = browser.query('body');
console.log(qunit_tests.innerHTML);
});
In the console output I do see the Qunit tests container <ol id="qunit-tests"></ol> but it is empty which means when the visit callback function is called, the tests haven't run.
I've tried to use the wait function to wait for the tests to run, but unsuccessfully:
function waitForQunitToEnd(window) {
var last = window.document.querySelector('selectorOfMyLastTest');
var first_failed = window.document.querySelector('li.failed');
return (last || first_failed);
}
browser.visit("http://localhost/mywebpage.html", function () {
browser.wait(waitForQunitToEnd, function() {
var qunit_tests = browser.query('body');
console.log(qunit_tests.innerHTML); // still gives me empty <ol id="qunit-tests"></ol>
});
});
I tried to play with the waitFor option (e.g. set to 5000ms) but that didn't help either.
Q1: Is what I'm trying to do making sense, or is there a much simpler way of doing something similar?
Q2: Do you know how I could get Zombie.js to wait for the Qunit tests to run?
I don't know if it will help you but look this : http://api.qunitjs.com/QUnit.done/
Related
I'm writing E2E tests in Cypress (version 12.3.0). I have a page with a table in a multi-step creation process that requires some data from back-end application. In some cases (rarely, but it occurs) the request gets stuck and the loader never disappears. The solution is simple: go back to the previous step and return to the "stuck" table. The request is sent anew and most likely receives a response - the process and the tests can proceed further. If the loader is not present, then going back and forth should be skipped (most of the times).
I managed to work around that with the code below, but I'm wondering if it could be done with some built-in Cypress functions and without explicit waiting. Unfortunately, I didn't find anything in the Cypress docs and on StackOverflow. I thought that maybe I could use the then function to work on a "conditionally present" element, but it fails on get, that's why I've used find on the jQuery ancestor element.
waitForTableData() {
return cy.get('.data-table')
.should('exist')
.then(table => {
if (this.loaderNotPresent(table)) {
return;
}
cy.wait(200)
.then(() => {
if (this.loaderNotPresent(table)) {
return;
}
cy.get('button')
.contains('Back')
.click()
.get('button')
.contains('Next')
.click()
.then(() => this.waitForTableData());
});
});
}
loaderNotPresent(table: JQuery) {
return !table.find('.loader')?.length;
}
Your code looks to me to be the best you could do at present.
The cy.wait(200) is about the right size, maybe a bit smaller would be better - 50 - 100 ms. The recursive call is going to give you similar behaviour to Cypress retry (which also waits internally, in order not to hammer the test runner).
Another approach would be to cy.intercept() and mock the backend, presuming it's the backend that gets stuck.
Also worth trying a simple test retry, if the loading only fails on a small percentage of times.
The Detox test framework, according to the documentation TroubleShooting Synchronization mentions that the test framework will wait and only perform your next test statement when the app is "Idle".
The problem I have right now is that my app never goes idle because there's some timeout that's continuously firing. It could be in my code or third-party code.
Here's an example of the output:
As you can see something is creating a new timer every few milliseconds. What'd I'd like to do is track down which one, I can see the ids but don't know a good way to try and track down exactly where this (setTimeout) id is coming from.
I thought of wrapping the setTimeout function, but not sure what's the best way to proceed with that.
I could eliminate or at least wrap setTimout in my own code, that way, at least determining it's a third-party lib (which still doesn't help solve the problem).
You could track down setTimeouts easily with:
// DEV mode only!
function failOnTimeout(func) {
const setTimeout = f => {
throw new Error(`Executing timeout \n ${f.toString()} \n`);
};
// A small trick to change scope:
eval("(" + func.toString() + ")()");
}
So you can do:
failOnTimeout(someMysteriousFunction);
And will give you stack traces of the first timeout set in that function (synchronously). You can then comment that out and search for the next one.
But instead of trackig down all timeouts, a quick solution could be to just throw an uncatched error to end the process.
tl;dr: When I run my test case, steps executed seem to work, but the test bails out early on a failure to find an element that hasn't even loaded yet. It seems like the waits I have around locating certain elements are loaded/launched as soon as the test is launched, not when the lines should actually be executed in the test case. I think this is happening because the page is barely (correctly) loaded before the "search" for the element to verify the page has loaded bails out. How do I wrangle the event loop?
This is probably a promise question, which is fine, but I don't understand what's going on. How do I implement my below code to work as expected? I'm working on creating automated E2E test cases using Jasmine2 and Protractor 5.3.0 in an Angular2 web app.
describe('hardware sets', () => {
it('TC3780:My_Test', async function() {
const testLogger = new CustomLogger('TC3780');
const PROJECT_ID = '65';
// Test Setup
browser.waitForAngularEnabled(false); // due to nature of angular project, the app never leaves zones, leaving a macrotask constantly running, thus protractor's niceness with angular is not working on our web app
// Navigate via URL to planviewer page for PROJECT_ID
await planListingPage.navigateTo(PROJECT_ID); // go to listing page for particular project
await planListingPage.clickIntoFirstRowPlans(); // go to first plan on listing page
await planViewerPage.clickOnSetItem('100'); // click on item id 100 in the plan
});
});
planViewerPage.po.ts function:
clickOnSetItem(id: string) {
element(by.id(id)).click();
browser.wait(until.visibilityOf(element(by.css('app-side-bar .card .info-content'))), 30000); // verify element I want to verify is present and visible
return expect(element(by.css('app-side-bar .card .info-content')).getText).toEqual(id); //Verify values match, This line specifically is failing.
}
This is the test case so far. I need more verification, but it is mostly done. I switched to using async function and awaits instead of the typical (done) and '.then(()=>{' statement chaining because I prefer not having to do a bunch of nesting to get things to execute in the right order. I come from a java background, so this insanity of having to force things to run in the order you write them is a bit much for me sometimes. I've been pointed to information like Mozilla's on event loop, but this line just confuses me more:
whenever a function runs, it cannot be pre-empted and will run entirely before any other code
runs (and can modify data the function manipulates).
Thus, why does it seem like test case is pre-evaluated and the timer's set off before any of the pages have been clicked on/loaded? I've implemented the solution here: tell Protractor to wait for the page before executing expect pretty much verbatim and it still doesn't wait.
Bonus question: Is there a way to output the event-loop's expected event execution and timestamps? Maybe then I could understand what it's doing.
The behavior
The code in your function is running asynchronously
clickOnSetItem(id: string) {
element(by.id(id)).click().then(function(){
return browser.wait(until.visibilityOf(element(by.css('app-side-bar .card .info-content'))), 30000);
}).then(function(){
expect(element(by.css('app-side-bar .card .info-content')).getText).toEqual(id);
}).catch(function(err){
console.log('Error: ' + err);
})
}
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!
efor this problem i am using Node-Webkit (node.js) and Async, loading a Windows App.
The reason of this question is to definitively answer:
What really means asynchronous execution in Javascript and Node.Js.
My personal code problem is at the end of the Question. "The Case".
I am going to explain all about the problem i have directly with a schematic summary. (And I will be updating the info as you help me to understand it)
The Concept (theory)
Imagine a Primary Screen (JS, Html, css, ... Node.js frameworks) and a Background Procedure (JS execution every 10 min, JS internal checks, background Database Optimization, ...).
Whatever you do in Primary Screen wont affect background execution (except some important cases), and Background can change even the Screen if he needs to (screen timers, info about online web status, ...)
Then the behaviour is like:
Thread 1: Your actions inside the App framework. Thread 2: Background App routines
Any action as they finish gives his output to screen, despite of the rest of the actions in async parallel
The Interpretation (For me)
I think this is something that "Async" will handle without problems, as a parallel execution.
async.parallel([
function(){ ... },
function(){ ... }
], callback); //optional callback
So the Thread 1 and Thread 2 can work together correctly while they do not affect the same code or instruction.
The Content will be changing while any threads request something of/to it.
The Implementation (Reality)
Code is not fully asynchronous during the execution, there are sync parts with common actions, that when they need calls the async codes.
Sync: Startup with containers -> Async: load multiple content and do general stuff -> Sync: Do an action in the screen -> ...
The Case
So here it is my not working properly code:
win.on('loaded', function() {
$( "#ContentProgram" ).load( "view/launcherWorkSpace.html", function() {
$("#bgLauncher").hide();
win.show();
async.parallel([
function() //**Background Process: Access to DB and return HTML content**
{
var datacontent = new data.GetActiveData();
var exeSQL = new data.conn(datacontent);
if(exeSQL.Res)
{
var r = exeSQL.Content;
if(r.Found)
{
logSalon = new data.activeSData(r)
$('#RelativeInfo').empty();
$("#RelativeInfo").html("<h4 class='text-success'>Data found: <b>" + logData.getName + "</b></h4>");
}
}
},
function() //**Foreground Process: See an effect on screen during load.**
{
$("#bgLauncher").fadeIn(400);
$("#centralAccess").delay(500).animate({bottom:0},200);
}
]);
});
});
As you can see, im not using "Callback()" because i dont need to (and it does the same).
I want to do the Foreground Process even if Background Process is not finished, but the result of the code is done at same time when both request has finished...
If i disconect the DB manually, first function takes 3 seconds until gives an exception (that i wont handle). Until then, both proccess will not output (show on screen) anything. (Foreground Process should be launched whatever happends to Background Process).
Thanks and sorry for so much explanation for something that looks like trivial.
EDITED
This start to be annoying... I tried without Async, just a javascript with callback like this:
launchEffect(function () {
var datacontent = new data.GetActiveData();
var exeSQL = new data.conn(datacontent);
if(exeSQL.Res)
{
var r = exeSQL.Content;
if(r.Found)
{
logData = new data.activeData(r)
$('#RelativeInfo').empty();
$("#RelativeInfo").html("<h4 class='text-success'>Salón: <b>" + log.getName + "</b></h4>");
}
}
});
});
});
function launchEffect(callback)
{
$("#bgLauncher").fadeIn(400);
$("#centralAccess").delay(500).animate({bottom:0},200);
callback();
}
Even with this... Jquery doesnt work until the callback answer...
node-webkit let's you run code written like code for node.js, but is ultimately just a shim running in WebKit's Javascript runtime and only has one thread, which means that most 'asynchronous' code will still block the execution of any other code.
If you were running node.js itself, you'd see different behavior because it can do genuinely asynchronous threading behind the scenes. If you want more threads, you'll need to supply them in your host app.