Using npm module cypress-wait-until, is there a way to catch errorMsg if cy.waitUntil() has completed all possible intervals and exceeded timeout? Thereby, providing a point of failure.
Example code waiting for an entry to be found in a log. Custom command cy.cmdAvailable() response is Chainable<boolean>, returns true when a specific service is yet available, else false:
cy.waitUntil(() => cy.cmdAvailable()), {
errorMsg: 'ERROR: Not Available',
verbose: true,
customCheckMessage: 'CHECK: Availability'
}).then($status => {
expect($status).to.be.a('boolean');
});
cy.waitUntil() always throws an error on timeout, so options are limited.
You could precede the command with a listener on the fail event - Recipe Error handling
If you want the status inside .then(), use a flag variable,
let failed = false;
cy.once('fail', (event, runnable) => {
if (e.message.includes('ERROR: Not Available')) {
failed = true;
return false // not fail on this message
})
})
cy.waitUntil(() => cy.cmdAvailable()), {
errorMsg: 'ERROR: Not Available',
verbose: true,
customCheckMessage: 'CHECK: Availability'
}).then(() => {
// use failed here
})
It's possible .then() does not get actioned because previous chained command failed.
If so, separate it from cy.waitUntil()
cy.waitUntil(() => cy.cmdAvailable()), {
errorMsg: 'ERROR: Not Available',
verbose: true,
customCheckMessage: 'CHECK: Availability'
})
cy.then(() => {
// use failed here
})
Related
I'm having an issue with a race condition in NgRx. In the example below, I'm asynchronously presenting a loading dialog at about the same time as I'm starting an async remote operation. But the remote operation has the potential to complete and fire dismissLoadingDialog() before the loading dialog is fully built, which results in a console error.
What might be a good strategy in NgRx to complete presentLoadingDialog() before the remote operation begins?
#Effect() fetchServerData$ = this.actions$.pipe(
ofType<FetchServerData>(ActionTypes.FetchServerData),
switchMap(action => {
this.presentLoadingDialog('...loading');
return this.dataService.fetchData(action.payload).pipe(
map(result => {
this.dismissLoadingDialog();
return new FetchServerDataSuccess(result);
}),
catchError(err => of(new FetchServerDataFail(err)))
);
})
);
async presentLoadingDialog(message: string): Promise<void> {
this.isLoading = true;
return this.loadingCtrl
.create({
duration: 5000,
message: message
})
.then(loadingDialog => {
loadingDialog.present().then(() => {
if (!this.isLoading) {
loadingDialog.dismiss();
}
});
});
}
async dismissLoadingDialog() {
this.isLoading = false;
if (!isNullOrUndefined(this.loadingCtrl)): Promise<boolean> {
return this.loadingCtrl.dismiss();
}
}
Ionic's LoadingController create method returns a Promise which resolves when loader creation is complete. You can therefore use it in your effect's Observable chain:
presentLoadingDialog(message: string) {
const loader = this.loadingCtrl
.create({
duration: 5000,
message: message
});
return loader.present();
}
dismissLoadingDialog() {
this.loadingCtrl.dismiss();
}
#Effect() fetchServerData$ = this.actions$.pipe(
ofType<FetchServerData>(ActionTypes.FetchServerData),
switchMap(action => forkJoin(from(this.presentLoadingDialog('...loading'), of(action)),
switchMap(([_, action]) => this.dataService.fetchData(action.payload).pipe(
tap(() => this.dismissLoadingDialog()),
map(result => new FetchServerDataSuccess(result)),
catchError(err => {
this.dismissLoadingDialog();
return of(new FetchServerDataFail(err))
})
))
);
The standard I have seen is you have loading and loaded flags in your state. When you dispatch a load action the reducer updates the state with loading: true and loaded: false before the action fires the http request. The action then switch maps to an action that updates the state with the response and loading: false and loaded: true.
In your component you then have a selector for the loading flag and subscribe to it to open and close the dialog
this.loadingSub = loadings$.subscribe(loading => {
if (loading) {
this.presentLoadingDialog('...loading');
} else {
this.loadingDialog.dismiss();
}
});
unsubscribe in onDestroy
It should be up to your components to show UI components, I think actions calling loading dialogs is not an action concern. Tapping into the heart of state management to call UI components is not a pattern I would recommend.
I have a very simple server polling scenario:
call API -> 2. onSuccess -> 3. wait 500ms -> 4. go back to step 1
and
call API -> 2. onError -> 3. finish
I want to be using rxjs as I already use rxjava. But I just can't seem to arrive on a proper solution for my problem.
I've tried timer and interval, but the problem is that they just run infinitely with no handlers to pause them while waiting for server to response or quit altogether when error occurs. Tried using retryWhen, but was not able to get it work at all.
This is what I want:
downloadData() {
console.log('downloading data')
$.getJSON('http://localhost:80')
.done((data) => {
console.log('done' + JSON.stringify(data))
setTimeout(() => { this.downloadData() }, 500)
}).fail(function (jqXHR, textStatus, errorThrown) {
console.log(`Error: ${textStatus}`)
})
}
How to achieve the same thing in rxjs?
You should take a look at the repeat operator:
fromFetch.fromFetch(`https://www.googleapis.com/books/v1/volumes?q=purple cow&maxResults=3`).pipe(
exhaustMap(response => {
if (response.ok) {
// OK return data
return response.json()
} else {
// Server is returning a status requiring the client to try something else.
return of({ error: true, message: `Error ${response.status}` })
}
}),
catchError(err => {
// Network or other error, handle appropriately
return of({ error: true, message: err.message })
}),
filter(resp => !resp.error)
delay(500),
repeat(),
).subscribe()
I am trying to write test for a model in sequelize, but I do not understand why it is not failing
it('should find user by id', (done) => {
users.findByPk(2)
.then((retrievedUser) => {
expect(retrievedUser.dataValues).to.deep.equal('it should break');
done();
})
.catch((err) => {
console.log(`something went wrong [should find user by id] ${err}`);
done();
})
});
When I run the test the output is the following
something went wrong [should find user by id] AssertionError: expected { Object (id, email, ...) } to deeply equal 'it should break'
1 -__,------,
0 -__| /\_/\
0 -_~|_( ^ .^)
-_ "" ""
1 passing (40ms)
If someone want to watch the full code, I created a project
For an asynchronous Mocha test to fail, pass an error as an argument to the done callback function
it('should find user by id', (done) => {
users.findByPk(2)
.then((retrievedUser) => {
expect(retrievedUser.dataValues).to.deep.equal('it should break');
done();
})
.catch((err) => {
console.log(`something went wrong [should find user by id] ${err}`);
done(err);
})
});
Alternatively, use an async function without a callback:
it('should find user by id', async () => {
const retrievedUser = await users.findByPk(2);
try {
expect(retrievedUser.dataValues).to.deep.equal('it should break');
} catch (err) {
console.log(`something went wrong [should find user by id] ${err}`);
throw err;
}
});
That said, I wouldn't recommend logging the error message of failing tests, because that's what Mocha already does for you in a typical setup. So I would get rid of the try-catch block in the example above.
it('should find user by id', async () => {
const retrievedUser = await users.findByPk(2);
expect(retrievedUser.dataValues).to.deep.equal('it should break');
});
I have a network call where it's likely that api will throw an 400 error. I want to handle this gracefully.
Right now I do it like below -
private fetchStatus(objectId: string): Observable<string> {
return Observable.create((observer) => {
this.http.get('/api/data-one').subscribe(response => {
if (response.result === 'SUCCESS') {
observer.next('SUCCESS');
} else {
observer.next('DENIED');
}
observer.complete();
},
error => {
observer.next('DENIED');
observer.complete();
});
});
}
But I will prefer doing it with Observable.map operator. The problem with Observable.map is when api throws a 400 the entire observable goes in error mode.
I want to prevent this because this get call is being used in a forkJoin with other calls. Failure of this would mean failure of the entire forkJoin below
forkJoin([
this.http.get('/api/route-2'),
this.http.get('/api/route-1'),
this.fetchStatus('abc')
]).subscribe((responseCollection: any) => {
observer.next({
result1: responseCollection[0],
result2: responseCollection[1],
result3: responseCollection[2]
});
observer.complete();
}, error => observer.error(error));
You can do this with map and catchError.
catchError will catch any error thrown by the source and return a new Observable. This new Observable is what, in your case, will be passed to forkJoin in the case of a HTTP error.
private fetchStatus(objectId: string): Observable<string> {
return this.http.get('/api/data-one').pipe(
map(response => response.result === 'SUCCESS' ? 'SUCCESS' : 'DENIED'),
catchError(error => of('DENIED')),
);
}
How can i with cypress show an custom error message when element is not present?
For the snippet below, i would like to display: "No rows are displayed" instead of the provided; "expected #rows to exist in the DOM".
cy.get('#rows').should('exist');
Cypress event handling gives a hook that may be used to customize the error message.
The Cypress log shows errors in the format ${error.name}:${error.message}. You can change both error properties, but the : is hard-coded.
Here are some samples,
describe('custom error', () => {
// Ref: https://docs.cypress.io/api/events/catalog-of-events.html#Catching-Test-Failures
it('fails with custom error message', () => {
cy.on('fail', (error, runnable) => {
error.name = 'CustomError'
error.message = 'Incorrect, 1 !== 2'
throw error // throw error to have test still fail
})
cy.wrap(1).should('eq', 2)
})
/*
Ref: https://docs.cypress.io/api/cypress-api/custom-commands.html#Child-Commands
Add this to /cypress/support/commands.js
*/
Cypress.Commands.add('onFail', { prevSubject: true }, (chainedSubject, message) => {
cy.on('fail', (error, runnable) => {
error.name = 'CustomError'
error.message = 'Incorrect, 1 !== 2'
throw error // throw error to have test still fail
})
return chainedSubject
})
it('fails with custom message via command', () => {
cy.wrap(1).onFail(customError).should('eq', 2)
})
/*
Ref: https://docs.cypress.io/api/cypress-api/custom-commands.html#Overwrite-Existing-Commands
Add this to /cypress/support/commands.js
*/
Cypress.Commands.overwrite('should', (originalFn, actual, assertion, expected, options) => {
if (options && options.message) {
cy.on('fail', (error, runnable) => {
error.name = 'CustomError'
error.message = options.message
throw error // throw error to have test still fail
})
}
return originalFn(actual, assertion, expected, options)
})
it.only('fails with custom message via overwrite of should', () => {
cy.wrap(1).should('eq', 2, { message: 'Incorrect: 1 !== 2'})
})
it('fails with standard message', () => {
cy.wrap(1).should('eq', 2)
})
})
It also works with cy.get()
This test uses a cy.get() and it also emits a custom message
it('fails with a custom message when using cy.get()', () => {
cy.visit('https://docs.cypress.io/api/commands/get.html')
cy.get('h1').onFail('Failed to find this text').should('contain', 'NoSuchText')
})