Unhandled promise rejections aren't properly reported in tests
Which @angular/* package(s) are the source of the bug?
Don't known / other
Is this a regression?
Yes
Description
In an Angular project that uses zone.js, unhandled promise rejections are not reported. In a zoneless Angular project, they are reported twice.
Steps to reproduce:
- Create a new Angular project.
- Paste the code below into a spec file.
-
npm t
it('triggers an unhandled rejection', function(done) {
Promise.reject(new Error('nope'));
// Make sure the unhandled rejection event has plenty of time to fire before
// Jasmine finishes running. This helps distinguish between timing issues
// (event is firing a bit later than usual) and the event not firing at all.
setTimeout(done, 1000);
});
Expected result: The spec should fail with a single "Unhandled promise rejection: Error: nope" error.
Observed:
- If the project was set up zoneless, the spec fails with two copies of the expected error.
- Otherwise, the spec passes.
Please provide a link to a minimal reproduction of the bug
https://github.com/sgravrock/ng-unhandled-rejection
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run ng version)
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 20.1.5
Node: 20.19.0
Package Manager: npm 10.8.2
OS: darwin arm64
Angular: 20.1.6
... common, compiler, compiler-cli, core, forms
... platform-browser, router
Package Version
------------------------------------------------------
@angular-devkit/architect 0.2001.5
@angular-devkit/core 20.1.5
@angular-devkit/schematics 20.1.5
@angular/build 20.1.5
@angular/cli 20.1.5
@schematics/angular 20.1.5
rxjs 7.8.2
typescript 5.8.3
zone.js 0.15.1
Anything else?
No response
The error is thrown asynchronously.
If you want to catch it in a zonefull app, you'll need fakeAsync:
it('triggers an unhandled rejection', fakeAsync(() =>{
Promise.reject(new Error('test error'));
}));
In a zoneless app, you'd have to await that promise :
it('triggers an unhandled rejection', async () => {
await Promise.reject(new Error('test error'));
});
@JeanMeche That's not quite the issue being described. I haven't looked at the problem when Zone.js is on the page, but it doesn't surprise me entirely (could be related to the global errors patch that at some point may have been put in place for this but doesn’t work anymore?? #63072 or maybe https://github.com/angular/angular/issues/51357#issuecomment-2555564572).
The issue with the zoneless setup is that jasmine is booted twice, once through jasmine-core and once through karma-jasmine, and this causes two of the global error handlers to be installed.
Right. The point of unhandled promise rejection detection is to catch cases where the rejection isn't propagated back to Jasmine due to bugs in the spec or the code under test. The feature was originally contributed to Jasmine by the Angular team, for what it's worth.
I took a deeper look into it.
Up to v16, the test fails with an Unhandled promise rejection. (Everytime it's logged twice probably for the reason you already mentionned @atscott)
From v17 onwards, the test passes.
Edit: Since zone.js release aren't tied to Angular version, the breakage was introduced in the transition between 0.14.1 and 0.14.2. The single change between those versions is 6d7eb3548c5fc3aedb4a52ff2010141343748e90
Great, thanks for confirming without a doubt that it’s https://github.com/angular/angular/issues/51357#issuecomment-2555564572
I think then that the ZoneJs portion of this bug already has a tracking issue (and finally I know why I occasionally come across errors that aren’t failing tests). The other portion is maybe a cli issue so I’ll transfer this to the cli repo.