Comments (22)
I'm also seeing this with Jest and a service provided at the component level after upgrading from Angular 8 and ngx-take-until-destroy
to @ngneat/until-destroy
and Angular 9. I'll see if I can create a repro tomorrow.
from until-destroy.
@arturovt @NetanelBasal - Here is a repro repository: https://github.com/brianmcd/until-destroy-bug
What I'm seeing is that if you provide the ExampleService
in a Component or Module's providers
array, then the test in that repository will fail with TypeError: Cannot read property 'onDestroy' of undefined
. If you provide the ExampleService
via providedIn: 'root'
or providedIn: 'any'
, then the test passes.
from until-destroy.
@Plysepter I have fixed this issue in this PR. @8.1.3
version has been published there.
from until-destroy.
Thanks @brianmcd. @arturovt I don't use nwrl nx in project I've got the error.
from until-destroy.
Can you reproduce it, please?
from until-destroy.
Sure @NetanelBasal. Jest is setup with 'jest-preset-angular' ver. 8.0.0.
package.json command: jest --config jest.config.js
Failed spec:
`import { CtdPopupLoginComponent } from 'layouts/ctdPopup/CtdPopupLoginComponent';
import { of } from 'rxjs';
import { createMockInstance } from 'setup/spyHelper';
import { EnvironmentService } from 'shared/services/abstract/environment/EnvironmentService';
import { UserService } from 'shared/services/abstract/user/UserService';
describe('CtdPopupLoginComponent', () => {
let component: CtdPopupLoginComponent;
let userServiceMock: jest.Mocked;
let environmentServiceMock: jest.Mocked;
let user: any;
beforeEach(() => {
jest.clearAllMocks();
user = {
name: 'test name',
};
userServiceMock = createMockInstance(UserService, {
user$: of(user),
});
environmentServiceMock = createMockInstance(EnvironmentService, {
environmentConfig: {
dashboard: 'https://test.api.com',
} as any,
});
component = new CtdPopupLoginComponent(
userServiceMock,
environmentServiceMock,
);
});
it('should be a defined component', () => {
expect(component).toBeTruthy();
});
it('should open new window with params', () => {
window.open = jest.fn();
component.goToLoginPage();
expect(window.open).toBeCalled();
const params = 'height=600,width=800,top=-300,
left=-400';
expect(window.open).toBeCalledWith('https://test.api.com', 'User login page', params);
});
});`
Error:
`Test suite failed to run
TypeError: Cannot read property 'onDestroy' of undefined
3 | import { tap } from 'rxjs/operators';
4 | import { EnvironmentService } from 'shared/services/abstract/environment/EnvironmentService';
5 | import { UserService } from 'shared/services/abstract/user/UserService';
| ^
6 |
7 | @UntilDestroy()
8 | @component({
at decorateDirective (node_modules/@ngneat/until-destroy/bundles/ngneat-until-destroy.umd.js:95:49)
at node_modules/@ngneat/until-destroy/bundles/ngneat-until-destroy.umd.js:105:17
at Object..__decorate (src/app/layouts/ctdPopup/CtdPopupLoginComponent.ts:5:95)
at Object. (src/app/layouts/ctdPopup/CtdPopupLoginComponent.ts:12:36)
at Object. (test/unit/platforms/ctd-extension/popup/CtdPopupLoginComponent.spec.ts:1:1)`
I guess, it's because component is created without TestBed (it's not a good arch of tests, but it worked until now and we have a lot of legacy in unit tests now, so it's not that easy to improve them that fast). Anyway the quick fix for that is:
jest.mock('@ngneat/until-destroy', () => ({ ...jest.requireActual('@ngneat/until-destroy'), UntilDestroy: jest.fn().mockImplementation((options: any = {}) => { return (target: any) => { if (!target.ɵprov) { target.ɵprov = true; } return jest.requireActual('@ngneat/until-destroy').UntilDestroy(options)(target); }; }), }));
Maybe there is a better way of checking Injectable than depending on target.ɵprov (injected by ivy compiler)?
from until-destroy.
Why it's a @component
and not @Component
?
from until-destroy.
It was formatted when I've pasted that. Its @Component
in source code and in error.
Sorry for that.
In source code:
`import { Component, OnDestroy } from '@angular/core';
import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';
import { tap } from 'rxjs/operators';
import { EnvironmentService } from 'shared/services/abstract/environment/EnvironmentService';
import { UserService } from 'shared/services/abstract/user/UserService';
@UntilDestroy()
@component({
selector: 'ctd-popup-login',
templateUrl: 'ctd-popup-login.html',
})
export class CtdPopupLoginComponent implements OnDestroy {`
Those services for tests are created with:
import createMockInstance1 from 'jest-create-mock-instance';
It's obviously problem with Jest environment, but it's because of usage of ivy flags in 'take-until'.
Probably more devs will have problem with that in other runtiime envs too, I guess.
from until-destroy.
We test the project with Jest, both the playground and the library, and it works fine.
from until-destroy.
Gentlemen, that would be awesome if you could create a minimal reproducible example for us.
from until-destroy.
Thank you, Brian! I will try to have a look during the week when I get some free time!
from until-destroy.
No problem. Thanks for looking into it, Artur. I should mention that I generated the repro project with Nx, since that's an easy way to get an app running with Jest. My real project that's hitting this bug is also using Nx. I don't think that should make a difference, but wanted to mention it just in case.
from until-destroy.
I was able to reproduce your problem. The issue is here:
ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points
Jest uses the CJS module system and those bundles are provided via UMD.
Before making any changes you can put this into app.component.spec.ts
:
import { ɵivyEnabled as ivyEnabled } from '@angular/core';
console.log(`ivyEnabled = ${ivyEnabled}`);
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
You will see that ivyEnabled = false
in your test environment.
You have to remove all flags because Angular Ivy guide mentions that:
Don't use --create-ivy-entry-points as this will cause Node not to resolve the Ivy version of the packages correctly.
Also --first-only
is not needed, from its description:
If specified then only the first matching
package.json property will be compiled
So basically that seems weird that you pass these options es2015 browser module main
with the --first-only
since es2015
will be taken.
So when I ran plain ngcc
command w/o anything and then yarn ng test --watch
I received that output:
from until-destroy.
Thanks so much for looking into this, @arturovt. I changed the postinstall to just run ngcc
and things are working now.
The postinstall command ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points
was added automatically by Nx, but is also mentioned here. Like you said, the docs do later mention that the flags will cause issue with module resolution in Node.
From digging through issues, it looks like Nx adds the postinstall so that ngcc runs before anything else, which was required for parallel builds. From what I could gather, the Angular CLI team is working on introducing a lockfile that should remove the need for the postinstall script for Nx users.
The relevant issues are:
It's also worth noting that the standard Angular CLI doesn't add any postinstall step, and it seems to only build esm2015 versions, so I think users of the stock CLI who use Jest will see this behavior, too.
from until-destroy.
Unfortunately in some cases --create-ivy-entry-points
is needed, as per this ticket angular/angular#35000 (comment)
from until-destroy.
Gentlemen, is it resolved? Can the issue be closed?
from until-destroy.
For me, everything is working with running ngcc
with no flags in the postinstall
step. Thanks again for your help, @arturovt.
from until-destroy.
I am not sure that this should be considered closed just yet. I've been experiencing this issue myself and running ngcc
without any parameters does remove the TypeError: Cannot read property 'onDestroy' of undefined
error but I instead encounter the following error when using jest's snapshot feature: PrettyFormatPluginError: Cannot read property 'element' of undefinedTypeError: Cannot read property 'element' of undefined
However if I remove the usage of this library from the code while using NX's default ngcc parameters, the tests all pass. I haven't been able to find the exact reasoning as to why snapshots of the fixture fail when the extra parameters have been added to the ngcc
command / when ivy is enabled. However this conflict is preventing my tests from passing and I would greatly appreciate if anyone else might have other suggestions or insights into the matter.
I have forked @brianmcd's example repo here and have added a few branches demonstrating the different variations which can be tested by removing and reinstalling the node_modules folder to recompile with ngcc followed by running the tests ng t
. Master is the base that has already been tested but updated with a snapshot test. The other branches are variations of the master branch but either with the ngcc parameters removed or this library commented out.
If anyone can help resolve this, I would be extremely grateful!
from until-destroy.
I had this issue as well. Changing the postinstall
script as suggested by @arturovt solved the primary error described above, but also introduced a new, i18n-related error:
It looks like your application or one of its dependencies is using i18n.
Angular 9 introduced a global `$localize()` function that needs to be loaded.
Please run `ng add @angular/localize` from the Angular CLI.
(For non-CLI projects, add `import '@angular/localize/init';` to your `polyfills.ts` file.
For server-side rendering applications add the import to your `main.server.ts` file.)
In total my tests kept on failing. Fortunately, the solution for this problem was more straight forward: You have to add the line
setupFiles: ['@angular/localize/init']
to the projects test configuration file jest.config.js
.
In case that's not clear enough, here's a small bash script to get a minimal example for this scenario up and running:
# Set up a simple nx workspace
npx create-nx-workspace@latest demo
? What to create in the new workspace angular
? Application name demo
? Default stylesheet format CSS
cd demo
# Add some i18n
sed "s/<h1>/<h1 i18n>/" -i apps/demo/src/app/app.component.html
ng add @angular/localize
# Install and use `until-destroy`
npm install @ngneat/until-destroy
sed -e "2i import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy';" \
-e "2i import { of } from 'rxjs';" \
-e "3i @UntilDestroy()" \
-e "10i obs$ = of().pipe(untilDestroyed(this));" \
-i apps/demo/src/app/app.component.ts
# Update the `postinstall` script to `ngcc`
sed "s/ngcc --properties es2015 browser module main --first-only --create-ivy-entry-points/ngcc/" -i package.json
rm -rf node_modules
npm install
# Update Jest config
sed "5i setupFiles: ['./jest.setup.ts']," -i apps/demo/jest.config.js
echo "import '@angular/localize/init';" > apps/demo/jest.setup.ts
# Tests now pass again
ng test
from until-destroy.
As an update on my report: It looks like there are compatibility issues within jest-preset-angular
and ivy in the serializer. I believe (at least in the minimum reproduction instance) that running ngcc
without any parameters will resolve my use case once thymikee/jest-preset-angular#351 is resolved
from until-destroy.
Thank you @arturovt, it is greatly appreciated!
from until-destroy.
I'm closing this issue. Please re-open with a reproduction if the problem still exists. Thanks.
from until-destroy.
Related Issues (20)
- unsubscribed checker breaks change detection HOT 1
- Add a util cancel Promises
- Angular 14 ut could not run correctly HOT 1
- Angular 14 ut could not run correctly with a minimal reproducible example
- Angular 14 ut could not run correctly with a a minimal reproducible example HOT 7
- Can I ask what's the utility for this package?
- How to use until-destroy-migration? HOT 4
- NG0204: Token InjectionToken X is missing a ɵprov definition after Angular 15 upgrade HOT 20
- Angular 15 Directive Composition API: Cannot access [component] before initialization. HOT 4
- [Documentation] For using `untilDestroyed` in non Angular apps HOT 1
- untilDestroyed assumes ngDevMode is defined HOT 2
- Warnings for github actions builds
- Component is undefined if @UntilDestroy() is used HOT 2
- Supporting other types of Subscriptions HOT 1
- Support TS 5.0 non experimental decorators for the UntilDestroy decorator
- ERROR Error: ASSERTION ERROR: token must be defined [Expected=> null != undefined <=Actual] HOT 5
- Angular 16 takeUntilDestroyed HOT 7
- Need upgrade for Angular 17 HOT 2
- Debugger in source code
- Error when using Target 2022 - Similar issue link provided HOT 9
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from until-destroy.