Protractor/Jasmin Stop execution or skip test if BeforeAll or BeforeEach fails - jasmine

Is there any way to Stop execution or skip test if there are any failures in BeforeAll or BeforeEach ? in Protractor+Jasmin framework
I have tried few things which are not helping like: protractor-fail-fast, jasmine-bail-fast, topSpecOnExpectationFailure: true
In my test if Login does not work in BeforeAll or BeforeEach Test are still getting executed.
Is there any why by which if Login fails it skips the execution in it() blocks.

i think there won't be nice solution here. I can suggest some hack like this:
describe('Some feature', function () {
preconditionFailed = false
beforeAll(async function () {
try {
await somePreconditions()
preconditionFailed = false
} catch(err) {
preconditionFailed = true
throw err
}
}
beforeEach(async function () {
try {
await somePreconditions()
preconditionFailed = false
} catch(err) {
preconditionFailed = true
throw err
}
}
it('should work', async feature() {
if(preconditionFailed) {
throw new Error('Precondition failed, cannot start test')
}
await someTestLogic()
})
})

Related

Uncaught SyntaxError: await is only valid in async functions and the top level bodies of modules

const accounts = await web3.eth.getAccounts();
App = {
load: async () => {
await App.loadWeb3(
await App.loadAccount()
)
},
loadWeb3: async () => {
if (typeof web3 !== 'undefined') {
App.web3Provider = web3.currentProvider
web3 = new Web3(web3.currentProvider)
} else {
window.alert("Please connect to Metamask.")
}
// Modern dapp browsers...
if (window.ethereum) {
window.web3 = new Web3(ethereum)
try {
// Request account access if needed
await ethereum.enable()
// Acccounts now exposed
web3.eth.sendTransaction({/* ... */})
} catch (error) {
// User denied account access...
}
}
// Legacy dapp browsers...
else if (window.web3) {
App.web3Provider = web3.currentProvider
window.web3 = new Web3(web3.currentProvider)
// Acccounts always exposed
web3.eth.sendTransaction({/* ... */})
}
// Non-dapp browsers...
else {
console.log('Non-Ethereum browser detected. You should consider trying MetaMask!')
}
},
loadAccount: async () => {
App.account = web3.eth.accounts[0]
console.log(App.account)
}
}
$(() => {
$(window).load(() => {
App.load()
})
})
The error is in LINE 1 where I get the accounts from Ganache but await is valid only for async.
What changes should I make in this code to remove the error? Please help me.
If I remove this line the error says that it cannot access accounts and after this await does not work.
Is there any way to make this piece of code in the form of an ASYNC function?
await calls can only be made in functions marked as async. As you have used await in line 1 it is not wrapped in an async function. You can wrap your code in a async function and then call that function. e.g something like:
const main = async () => { // <- the async wrapper function
const accounts = await web3.eth.getAccounts();
// .... rest of your code
$(() => {
$(window).load(() => {
App.load()
})
})
}
main()
Or if you want to be more advanced and not save the function at all
(async ()=>{
const accounts = await web3.eth.getAccounts();
// .... rest of your code
})() // <- call the function right after declaring it

How to retry fetch in a loop when it throws an error

I have the following JS function:
func() {
return fetch({
...
}).then({
...
})catch({
...
})
}
In it I return a promise returned by fetch(). In the event that it fails (ie calls catch() block) I want to repeat the whole thing. Something like having the whole thing in a while (true) loop, but I can't figure out how to do this with promises.
Any suggestions?
you should have a close look to promises and async await.
async function fetchUntilSucceeded() {
let success = false;
while(!success) {
try {
let result = await fetch(...);
success = true;
//do your stuff with your result here
} catch {
//do your catch stuff here
}
}
}
If you just need the results:
async function fetchUntilSucceeded() {
while(true) {
try {
return await fetch(...);
}
}
}
But be careful with such code as it might never resolve! also it can send a lot of requests without any waittime in between.
You can simply write a loop and count down the attempts until one succeeds or you run out. async/await makes this easy. See below for a minimal, complete example.
Note that the fetch API uses the response.ok flag to ensure that the response status falls in the 200 range. Wrapping with a try/catch is only sufficient to cover connection failures. If the response indicates a bad request, a retry is likely inappropriate. This code resolves the promise in such cases but you could consider !response.ok as an error and retry if you wish.
const fetchWithRetry = async (url, opts, tries=2) => {
const errs = [];
for (let i = 0; i < tries; i++) {
// log for illustration
console.log(`trying GET '${url}' [${i + 1} of ${tries}]`);
try {
return await fetch(url, opts);
}
catch (err) {
errs.push(err);
}
}
throw errs;
};
fetchWithRetry("https://httpstat.us/400")
.then(response => console.log("response is OK? " + response.ok))
.catch(err => console.error(err));
fetchWithRetry("foo")
.catch(err => console.error(err.map(e => e.toString())));
fetchWithRetry("https://httpstat.us/200")
.then(response => response.text())
.then(data => console.log(data))
.catch(err => console.error(err));
Pass the tries parameter as -1 if you want an infinite number of retries (but this doesn't seem like the common case to me).

Can't write out protractor network/performance log in a custom jasmine reporter

I'm trying to write to console the network log after a test failure as part of my protractor suite. The code works fine when in an afterEach() block but fails to execute the promise when inside of a custom jasmine reporter. As far as I can tell the promise never executes, but there are no known/shown errors.
protractor config (simplified):
exports.config = {
specs: ['./e2e/**/*.spec.ts'],
capabilities: {
browserName: 'chrome',
chromeOptions: {
perfLoggingPrefs: {
enableNetwork: true,
enablePage: false,
}
},
loggingPrefs: {
performance: 'ALL',
browser: 'ALL'
},
},
onPrepare() {
jasmine.getEnv().addReporter({
specDone: result => {
new ErrorReporter(browser).logNetworkError(result);
}
});
},
};
ErrorReporter:
class ErrorReporter {
constructor(browser) {
this.browser = browser;
}
logNetworkError(result) {
if(result.status === 'failed') {
// execution makes it in here
this.browser.manage().logs().get('performance').then(function(browserLogs) {
// execution DOES NOT make it here
browserLogs.forEach(function(log) {
const message = JSON.parse(log.message).message;
if(message.method === 'Network.responseReceived') {
const status = message.params.response.status;
const url = message.params.response.url;
if(status !== 200 && status !== 304) {
console.log(`----E2E NETWORK ERROR----`);
console.log(`STATUS: [${status}]`);
console.log(`URL: [${url}]`);
console.log(`RESPONSE: [${log.message}]`);
}
}
});
});
}
}
}
module.exports = ErrorReporter;
The code inside the logNetworkError() method works completely fine when executed in an afterEach() block but never writes out any logs when executed as a custom reporter. I would expect that this would work as a jasmine reporter as well.
If it's not possible to execute this as a jasmine reporter is there some way to access the executed test's results in the afterEach() block? I do not want to log on successful test execution.
I figured out the solution. There was 2 main problems. The first was that I needed to use async and await inside of the function that was creating the log:
async logNetworkError(result) {
if(result.status === 'failed') {
const logs = await this.browser.manage().logs().get('performance');
logs.forEach((log) => {
const message = JSON.parse(log.message).message;
if(message.method === 'Network.responseReceived') {
const status = message.params.response.status;
const url = message.params.response.url;
if(status !== 200 && status !== 304) {
console.log(`-----E2E NETWORK ERROR-----`);
console.log(`TEST NAME: ${result.fullName}`);
console.log(`STATUS: [${status}]`);
console.log(`URL: [${url}]`);
console.log(`RESPONSE: [${log.message}]`);
}
}
});
}
}
the second part of the problem was that another reporter which was saving screenshots did not have async and await which was stopping other reporters from completing. Adding async/await to both reporters solved this issue.

How to spyOn a function which has an Promise inside and don't return the result but handles the response itself?

How can I spyOn a method called placed in the object?
// Game.js
export default {
mine: null,
handle: function(me) {
console.log(" FOOOOO " + me)
},
setSource: function() {
this.mine.getSource().then((response) => {
const {source} = response
this.handle(source)
})
}
}
Here i try to spy:
// GameSpec.js
import Game from '../../lib/jasmine_examples/Game'
Game.mine = {}
describe("Game", function() {
it("should set source and handle it", function() {
Game.mine.getSource = () => {
return new Promise((resolve)=>{
resolve( {
source : 'BAAAAR'
})
})
}
spyOn(Game, 'handle').and.callThrough()
Game.setSource()
expect(Game.handle).toHaveBeenCalled()
});
});
In the output you can see the function "handle" was called:
Started
F FOOOOO BAAAAR
Failures:
1) Game should set source and handle it
Message:
Expected spy handle to have been called.
Stack:
Error: Expected spy handle to have been called.
at <Jasmine>
at UserContext.<anonymous> (/Users/silverbook/Sites/zTest/jasmine/spec/jasmine_examples/PlayerSpec.js:20:29)
at <Jasmine>
1 spec, 1 failure
But jasmine says it was not called.
If i remove the mocked Promise the test passes but i needed there. In another test i will return an error in the Promise and let it handle from another function.
So the Promise breaks the test but why?
The test executes synchronously and the expect fails before the callback queued by this.mine.getSource().then() has a chance to execute.
For Jasmine >= 2.7 and async function support you can convert your test function into an async function and add an await Promise.resolve(); where you want to pause the synchronous test and let any queued callbacks execute.
For your test it would look like this:
import Game from '../../lib/jasmine_examples/Game'
Game.mine = {}
describe("Game", function() {
it("should set source and handle it", async () => {
Game.mine.getSource = () => {
return new Promise((resolve)=>{
resolve( {
source : 'BAAAAR'
})
})
}
spyOn(Game, 'handle').and.callThrough();
Game.setSource();
await Promise.resolve(); // let the event loop cycle
expect(Game.handle).toHaveBeenCalled();
});
});
For older (>= 2.0) Jasmine versions you can use done() like this:
import Game from '../../lib/jasmine_examples/Game'
Game.mine = {}
describe("Game", function() {
it("should set source and handle it", (done) => {
Game.mine.getSource = () => {
return new Promise((resolve)=>{
resolve( {
source : 'BAAAAR'
})
})
}
spyOn(Game, 'handle').and.callThrough();
Game.setSource();
Promise.resolve().then(() => {
expect(Game.handle).toHaveBeenCalled();
done();
});
});
});
You can run the test inside fakeAsync and run tick() before the expect()
Service:
getFirebaseDoc() {
this.db.firestore.doc('some-doc').get()
.then(this.getFirebaseDocThen)
.catch(this.getFirebaseDocCatch);
}
Unit testing:
it('should call getFirebaseDocThen', fakeAsync(() => { // note `fakeAsync`
spyOn(service, 'getFirebaseDocThen');
spyOn(service.db.firestore, 'doc').and.returnValue({
get: (): any => {
return new Promise((resolve: any, reject: any): any => {
return resolve({ exists: true });
});
},
});
service.getFirebaseDoc();
tick(); // note `tick()`
expect(service.getFirebaseDocThen).toHaveBeenCalled();
}));

How to run Promise test in Jasmine

I'm trying to test a promise in a separate library I injected to my app.
function myFunc(input) {
return new Promise(function(resolve, reject) {
···
resolve(value); // success
···
reject(error); // failure
});
};
This is my function that returns a Promise.
I would seriously love to run the test in jasmine like this
describe('Service: myService', function () {
var $log;
beforeEach(inject(function (_$log_) {
$log = _$log_;
}));
it('should get results', function () {
$log.log("start test");
var self = this;
myFunc(input).then(function(response) {
$log.log("success");
expect(response).toBe("response");
done();
}).catch(function(error) {
$log.log("fail");
self.fail(error);
done();
});
$log.log("end test");
});
});
My test passes(not expected.) and the only thing in my log is [start test] and [end test] as if the promise is totally ignored.
Since I'm not using $q for the promise, most jasmine tips angular doesn't seem to be helpful.
Any ideas on how to get into that 'then'?
Thanks
I could be wrong here, but this is most likely because you are attempting to test an asynchronous process here. So essentially what is happening, is that you call the function, but the test continues running and finishes before the promise ever returns, which is why the test succeeds.
One way to work around this (this is more like a hack, I'm sure there is a better way to do this, and if I find it, I will edit this post) is to add this bit of code at the end of your test:
describe("baseline test",function(){
it("baseline",function(done){
setTimeout(function(){
expect(1).toEqual(1);
done();
},1000);
});
});
Essentially what this snippet does is just set a timeout that waits for 1 second for any asynchronous calls to call back. If 1 second isn't enough, you can always increase that 1000 (which is in milliseconds). If this doesn't work, maybe look into adding another test suite that won't complete until the promises return.
TypeScipt code (Angular 8) to be tested
import { Injectable } from '#angular/core';
import { KeycloakService } from 'keycloak-angular';
import { environment } from 'environments/environment';
import { LoggedInUserHelperService } from 'shared/helper/logged-in-user-helper.service';
#Injectable({
providedIn: 'root'
})
export class AppInitializationService {
constructor(public keycloakService: KeycloakService) {
}
initApplication(): Promise<any> {
return new Promise<any>(
async (resolve: any, reject: any): Promise<any> => {
await this.initKeycloak()
.then(() => keyCloakInitialized = true)
.catch((error: Error) => {
console.error(`Couldn\'t initialize Keycloak Service. (Error: ${error})`);
reject(error);
return;
});
resolve();
}
);
}
private async initKeycloak(): Promise<any> {
return this.keycloakService.init({
config: environment.keycloak,
initOptions: {
onLoad: 'login-required',
checkLoginIframe: false,
promiseType: 'legacy'
},
enableBearerInterceptor: true,
bearerExcludedUrls: ['/assets']
});
}
}
Tests
import { TestBed } from '#angular/core/testing';
import { AppInitializationService } from 'app/app-initialization.service';
import { KeycloakService } from 'keycloak-angular';
describe('AppInitializationService', () => {
let testObj: AppInitializationService;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
KeycloakService
]
});
testObj = TestBed.get(AppInitializationService);
});
it('should call keycloak service init', async () => {
const spy = spyOn(testObj.keycloakService, 'init').and.returnValue(Promise.resolve(true));
await testObj.initApplication();
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith({
config: environment.keycloak,
initOptions: {
onLoad: 'login-required',
checkLoginIframe: false,
promiseType: 'legacy'
},
enableBearerInterceptor: true,
bearerExcludedUrls: ['/assets']
});
});
it('should log error on failed keycloak initialization', async () => {
const errorMsg = 'error-msg';
const spy1 = spyOn(testObj.keycloakService, 'init').and.callFake(
() => Promise.reject(errorMsg));
const spy2 = spyOn(console, 'error');
await testObj.initApplication().catch(() => { return; });
expect(spy1).toHaveBeenCalledTimes(1);
expect(spy2).toHaveBeenCalledTimes(1);
expect(spy2).toHaveBeenCalledWith(`Couldn\'t initialize Keycloak Service. (Error: ${errorMsg})`);
});
});

Resources