I'd like to use Async/await and onPrepare to make sure my tests don't start until onPrepare is complete - async-await

I want onPrepare to finish before any tests are run and I'm using async / await.
I'm new to javascript and protractor but I've been writing test automation for a couple of decades. This seems like an incredibly simple thing to want to do, have onPrepare finish before a test starts, but I'm confused my everything I've seen out there.
I've set SELENIUM_PROMISE_MANAGER: false so I don't want to use promises to do this, right?
My landing page in anguler
do I use async and await on onPrepare or browser.driver.wait or webdriver.until.elementLocated? If so, do I put 'await' before those waits? (That seems redundant)
onPrepare: async() => {
await browser.driver.get( 'https://localhost:8443/support-tool/#/service/config');
await browser.driver.findElements(by.className('mat-table'));
browser.driver.wait(webdriver.until.elementLocated(by.css('mat-select-value')), 10000);//(Returns webdriver not defined)
},
first, I get webdriver not defined when I run it. Once I get it to work, will my tests wait for onPrepare to be completed before they start running?

So Protractor is a wrapper for the webdriverJS and webdriverJS is completely asynchronous. To give a very high level definition, Protractor wraps webdriverJS up so that every action returns a promise (for instance .get(), .sendKeys(), .findElement()).
Previously webdriverJS had what is referred to as the 'control flow' which allowed users to write code as they would in any synchronous programming language and the fact the almost everything is a promise was handled behind the scenes. This feature has been deprecated in the latest versions and the main reason is that the introduction of the async/await style of handling promises makes it much easier for users to manage promises ourselves.
If you are using protractor 6.0+ the control flow is disabled by default but it will be disabled for you regardless as you have you have set SELENIUM_PROMISE_MANAGER: false. You will need to manually manage your promises, which you are doing, by using async/await.
browser.driver vs browser
I also want to point out the by using browser.driver.get you are referring to the underlying selenium instance and not the protractor wrapper instance therefore it will not wait for the angular page to stabilize before interacting (I could be mistaken on this). There is more info on the distinction in this thread.
Any action that involves the browser or the file system will likely be a promise so include the await before them and any function that contains an await needs to be declared async.
I would write your code as follows:
onPrepare: async() => {
await browser.get('https://localhost:8443/support-tool/#/service/config');
let someElement = await element(by.css('mat-select-value'));
await browser.wait(protractor.ExpectedConditions.presenceOf(someElement), 10000);
},
Finally, as long is your onPrepare is using awaits properly it should for sure complete before your tests begin.
Hope that helps and is clear, it was longer than I anticipated.

Related

Cypress tests failing in firefox in pages with polling requests

I have a following test in Cypress:
visit first page with the header A
click on the Go to B Page button
assert that the header of the page is now B
It works fine in Chrome, but failing in Firefox, as on the page B I have some background polling requests, and when cypress switches to another test and those requests get "canceled" away, I get either TypeError: NetworkError when attempting to fetch resource or AbortError: The operation was aborted
All the requests are using fetch api, by the way.
The possibility to mute those errors through the uncaught:exception event seems a bad idea, and so does the idea to do something on the page to cancel the polling, as it is not the thing under testing.
Maybe someone has encoutnered this problem too and got some non-hacky solution?
I had a similar issue with Cypress tests in Firefox and resorted to the slightly hacky solution of using an uncaught:exception handler as you mention. It is possible to filter error messages somewhat at least:
function handleUncaughtException(err){
if (err.message.includes('Request aborted') ) {
console.log("Request aborted. Test will continue. Error:",err);
return false; // return false to make test continue
}
throw err;
}
cy.on('uncaught:exception',handleUncaughtException);
In principle you can cancel this handler when it's no longer needed. In my case though, this stopped the test working, presumably because the request started previous to or after the calls.
cy.removeListener("uncaught:exception", handleUncaughtException)
The Cypress docs have some advice on defining these: see at https://docs.cypress.io/api/events/catalog-of-events#Examples. It may be useful to put the handler in a support file, so that it is applied to all tests.
(See also https://docs.cypress.io/api/events/catalog-of-events#Event-Types and https://nodejs.org/api/events.html#events_emitter_removelistener_eventname_listener).

Why stub in cypress may not work for route called after page was loaded

I am using cypress to write tests and have a problem which doesn't appear in every test. In some cases it works and I don't know why. So...
The Problem:
I defined a route and alias for it in beforeEach:
beforeEach(function () {
cy.server()
cy.route('GET', '/favourites?funcName=columnPreset', []).as('columnPresetEmpty')
cy.visit('#/search')
})
Stub works fine if http request occured on page load.
But if I perform request responding to click event (modal dialog opens and executes http request) it just appear in commands not makred as stubbed and following cy.wait('#columnPresetEmpty') fails with request timeout.
it('does not work', function () {
cy.get('[data-test=button-gridSettings]').click()
cy.wait('#columnPresetEmpty')
})
At the same time in other tests I have almost similar functionality where request is performed just by clicking on a button, without opening new modal window. It's the only difference.
What am I doing wrong?
The issue might be cypress can not yet fully handle fetch calls. You can disable it the following way but make sure you have fetch polyfill. This will then issue XHR requests which cypress can observe.
cy.visit('#/search', {
onBeforeLoad: (win) => {
win.fetch = null
}
})
More to read here:
https://github.com/cypress-io/cypress/issues/95#issuecomment-281273126
I found the reason causing such behavior. Problem was not in a modal window itself, but code performing second request was called in promise's callback of another request. Something like:
fetch('/initData')
.then(loadView)
And loadView function executed second fetch.
So when I removed loadView from promise's callback both requests become visible for cypress.
For info, I tried it out on my search modal (in a Vue app) and it works ok.
What I did:
created a dummy file named test-get-in-modal.txt in the app's static folder
added an http.get('test-get-in-modal.txt') inside the modal code, so it only runs after the modal is open
in the spec, did a cy.server(), cy.route('GET', 'test-get-in-modal.txt', []).as('testGetInModal') in a before()
in the it() added cy.wait('#testGetInModal') which succeeded
changed to cy.route('GET', 'not-the-file-you-are-looking-for.txt'..., which failed as expected
The only difference I can see is that I cy.visit() the page prior to cy.server(), which is not the documented pattern but seems to be ok in this scenario.

Using something other than cy.request to seed data in Cypress

I'm using Cypress for end-to-end testing. In my beforeEach, I'm using an SDK I've been provided to seed data on a server (the SDK sends API calls to the server but does not use cy.request inside it). The method on the SDK returns a promise, therefore I figured I could return the promise like so:
beforeEach(() => {
return sdk.createProperty(...);
});
My test then does something like this:
it('displays a property', () => {
cy.visit(`/companies/${appTestData.companyId}/properties`);
...the rest is commented out currently...
}
This actually works as intended, that is, it waits until the server response is returned before running the tests, but I see the following warning in the console when the test actually runs:
Cypress Warning: Cypress detected that you returned a promise in a test, but also invoked one or more cy commands inside of that promise...
I noticed if I change my beforeEach to use cy.then, the warning goes away:
beforeEach(() => {
cy.then(() => sdk.createProperty(...));
});
It seems a bit unnecessary and was kind of a stab in the dark, so I'd like to know if there's a prescribed way of doing what I need to do. I can't change the SDK I'm using to use cy.request, which I assume would also prevent the warning. Thanks.
Probably not what you want to hear, but can I confirm you that using cy.then(...) is the most standard way of waiting for a Promise in Cypress I know of.
After reading your question, I have tried to use Cypress Network Requests features to wait for a fetch('my/url') in a before(), but it doesn't seem to be detecting the request at all.

What is the difference between Zombie.js and Jasmine?

May I know what is the difference between Zombie.js and Jasmine? are they both frameworks?
Jasmine is a unit test framework for BDD (behavior driven development). It requires an execution environment like NodeJs or a browser like Firefox, Chrome, IE, PhantomJS etc. to run (and to provide the environment for the code under test). Jasmine provides the test execution and assertion infrastructure (that's the describe(), it(), expect()).
Zombie.js is an emulated, headless browser. It's a browser on its own plus an interaction API for itself. It's like Selenium/Webdriver. It's using jsdom under its hood to provide the APIs browsers usually provide. Zombie.js requires a test execution and assertion infrastructure (like Mocha + should.js or even Jasmine).
With Jasmine you write tests on a module or group-of-modules level. But usually not on an application level
With Zombie.js you interact with a website (served by a server) through an interaction API.
With Jasmine you make fine grained assertions on the output or events created for certain input - on the module level.
With Zombie.js you interact with the whole application (or website).
With Jasmine you test only the Javascript part.
With Zombie.js you test the the frontent + backend. Though you might be able to mock away and intercept server interaction (maybe, I'm not familar with it).
With Jasmine you call a method/function, pass a parameter and test the return value and events
With Zombie.js you load a page and fill a form and test the output
With Jasmine you need to run the tests in the proper execution envrionment (like Firefox, Chrome, ...)
With Zombie.js you pages runs in a new execution environment
With Jasmine you can test in browsers (consumers use) with their typical quirks
With Zombie.js you test you application in a new browser with new quirks
Jasmine example:
// spy on other module to know "method" was called on it
spyOn(otherModule, "method");
// create module
let module = new Module(otherModule),
returnValue;
// calls otherModule.method() with the passed value too; always returns 42
returnValue = module(31415);
// assert result and interaction with other modules
expect(returnValue).toBe(42);
expect(otherModule.method).toHaveBeenCalledWith(31415);
Zombie.js example:
// create browser
const browser = new Browser();
// load page by url
browser.visit('/signup', function() {
browser
// enter form data by name/CSS selectors
.fill('email', 'zombie#underworld.dead')
.fill('password', 'eat-the-living')
// interact, press a button
.pressButton('Sign Me Up!', done);
});
// actual test for output data
browser.assert.text('title', 'Welcome To Brains Depot');
Zombie.js, like Webdriver/Selenium, is no replacement for a unit testing framework like Jasmine, Mocha.

Should I use HTTP or xmlhttprequest on node.js? When?

I'm still exploring REST, node.js and generally web development. What I found out is that xmlhttprequest is mostly(if not always) used when using AJAX. As I learned AJAX is for asynchronous Javascript and XML. So my question is should I be using xmlhttprequest in my node.js project, just when I want to do asynchronous parts on my webpage? or does node.js HTTP also have opportunity to asynchronous javascript? How can I balance well the use of HTTP and xmlhttprequest(or AJAX) so that I don't get too messy in all my REST API stuff?
P.S. I kinda don't want to use AJAX, because of XML. I have heard that XML is much heavier in data than JSON and isn't worth using anymore. Is it true? What would you recommend me to do?
non async on node?
you're trying to build an endpoint api so all the other cases of not using async should be thrown out the window. As soon as you have a single non async code in your node.js project it will freeze the entire process until it is complete. Remember Node.js runs a single Thread (theoretically) which means all the other concurrent users are gonna get frozen.. that's one way to make people really upset.
say for instance you need to read a file from your Node.js server on a get request from a client (let's say a browser) well you want to make it a callback/promise never do non-async with an API server there is just no reason not to (in your case).
example below
import * as express from "express";
import * as fs from 'fs';
let app = express();
app.get('/getFileInfo', function(req, res) {
fs.readFile('filePath', 'UTF-8', function(err, data) {
if (err) {
console.log(err);
res.json({error: err});
} else {
res.json({data: data});
}
})
});
//users will freeze while the file is read until it is done reading
app.get('/nonasync', function(req, res) {
let data = fs.readFileSync('path', 'utf-8');
res.json({data:data});
});
the exact same idea applies to your web browser.. if you are going to not do something async in the browsers javascript the entire web application will be unresponsive because it also runs in the same manner, it has one main loop and unless they are in callbacks/promises/observable the website will freeze. Ajax is a much neater/nicer way to implement post/get/put/delete/get:id from a server then an XMLHttpRequest. now both of these have an option to send and receive JSON not only XML. Ajax is safer due to supporting different browser compatibility specs as XMLHttpRequest has some limitations in IE and Safari I believe.
NOTE: if you're not using a framework with node.js you should, it helps keep your endpoints neat and testable along with being able to pass the project on to others without them having to learn the way you implemented your req, res structure
some frameworks for node
Express 4 (my preference, api doc is really really good and strong
community)
Restify (used by Netflix - really light)
Hapi (never used but heard of)
some frameworks for web browsers you might like
angular 2 (my preference as I'm from a MEAN stack)
reactJS (created by big blue Facebook)
knockoutJS (simple and easy)
all the browser frameworks have their own implementation of the RESTful api's, but more are leaning towards Observable objects.

Resources