ngneat / spectator

🦊 🚀 A Powerful Tool to Simplify Your Angular Tests
https://ngneat.github.io/spectator
MIT License
2.1k stars 178 forks source link

Host-based test fails with "Can't bind to 'formControl' since it isn't a known property" in v9 / Angular 13 #521

Closed thw0rted closed 2 years ago

thw0rted commented 2 years ago

Is this a regression?

Yes

Description

I use createHostFactory to create hosts for testing a component that implements ControlValueAccessor. In the previous release (Spectator 7.2 / Angular 12) this worked fine, but after upgrading everything, an error is logged at runtime and bindings are not actually made so the tests all fail.

Please provide a link to a minimal reproduction of the bug

https://github.com/thw0rted/issue-examples/tree/spectator-cva

Please provide the exception or error you saw

> ERROR: 'NG0303: Can't bind to 'formControl' since it isn't a known property of 'some-component'.'

Please provide the environment you discovered this bug in

Angular13, Karma + karma-webpack

Anything else?

The linked repro shows that binding to formControl works in the consuming application but the host component binding fails. I don't know enough about troubleshooting the library to pinpoint a root cause; if you can help me understand why it's failing I would be willing to try to fix it myself.

Do you want to create a pull request?

No

thw0rted commented 2 years ago

Just for continuity, a while back I posted https://github.com/ngneat/spectator/discussions/481 where I was trying to get this working with Karma + karma-typescript (instead of karma-webpack) but never really got anywhere. I always worry that my setup is "wrong" in some way; if anybody could link me to an "ejected" (webpack config, not angular.json) starter repo that I can just check out and run npm run test, and execute a simple host-based test successfully, I'd be very grateful. (That'd also be a great repo to link from the main documentation somewhere...)

thw0rted commented 2 years ago

Update: I made a branch that uses Angular 12 and Spectator 7 with no other changes. The tests pass in this version, just to show that this is a regression.

NetanelBasal commented 2 years ago

Since we haven't touched this area for a long time, I don't believe it is related to the library.

thw0rted commented 2 years ago

Hi @NetanelBasal , the problem is that if I go to Angular and say "this is a regression with v13", I expect them to tell me that they don't know anything about Spectator and don't support it. Are you positive that you weren't using any non-public APIs that they might have changed? If it's not related to Spectator, what else could it be?

Like I said, my main concern is that I'm not confident that my project setup is "correct", and that I've had trouble in the past trying to debug how Spectator works. Did you have a chance to check out my repro branches? Is the test runner set up the way you'd expect? Alternately, do you maybe have a project using Spectator with AngularCompilerPlugin that you could point me to, and I could try to reproduce the issue using their setup? I just want some kind of path forward here.

NetanelBasal commented 2 years ago

I don't know. It seems like you are the only one who encountered this error. Let's wait and see if there are more like you.

I'll try to check your repo when I can.

benelliott commented 2 years ago

Hi @thw0rted, I'm a bit confused by your project setup - it doesn't seem to list @angular/core in its dependencies for a start which seems a little odd?

Out of curiosity, can I ask why you're trying to avoid angular.json etc?

thw0rted commented 2 years ago

Sorry for the confusion! The Angular 13 (spectaror-cva) branch should be fixed now, I think.

I appear to have installed those packages without saving them to package.json while initially creating the repro, then subsequent npm installs didn't remove them, I guess thanks to the lockfile? I had to add the packages explicitly when I made the Angular 12 (spectator-cva-angular12) branch, but thought at the time that this was due to a restructuring of dependencies between v12 and v13.

I'm avoiding angular.json etc because we have other build steps in our toolchain that rely on "vanilla" Webpack, stuff we set up way back around Angular v2. If supporting ejected projects becomes a problem, I could look at porting "into" ngcc... but all things being equal, I prefer my build to involve as little magic as possible.

benelliott commented 2 years ago

Fair enough... I got curious about this stuff after reading your post and ended up bumping into you in other issue threads actually such as https://github.com/angular/angular/issues/44026 😄

I'm still playing around with your demo but it smells to me like yet another upstream issue caused by the frustratingly opaque breaking changes that A13 introduced... There are a lot of steps in my own (not that niche) workflows that have been quite disrupted by the new compilation patterns. Will let you know on the off chance that I have any bright ideas!

benelliott commented 2 years ago

Btw - I did spend quite a lot of time playing with this and didn't manage to get Spectator fully working - however I made some headway with the vanilla testing utils by setting jitMode in your Webpack config:

...
        new AngularWebpackPlugin({
            tsconfig: `./tsconfig.${isTest ? "test" : "main"}.json`,
            jitMode: isTest
        }),
...

I think this eliminates the need for this workaround:

// NB: side-effect import of Module first so that Ivy compiles Hello component
// using the correct `imports` (e.g. FormsModule), otherwise it complains about
// `ngModel` being an unrecognized input
import "./app.module";

To investigate Spectator I copied the source from createHostFactory into the spec file and started trying to add some await getTestBed().compileComponents() statements in suspicious places. No luck yet (confusingly, debugging the TestBed compiler suggests that all the imports and declarations are loaded in correctly) but maybe some food for thought if you want to do some more digging @thw0rted !

johncrim commented 2 years ago

I had the same error - I had errors binding to formControl or ngIf directives, but I had no problems binding to my own directives. I had these problems when not using angular CLI for pulling in the library - both with Cypress, and using runtime compilation.

The reason you're getting this error is that the library (@angular/forms) hasn't being "linked" at build time, so the directive metadata can't be found (so they can't be bound). The linking is required (which is new in ng 13) - see

For background on partial Ivy format. The first article give you some symbols to look for in the library, which you can use to confirm that the library hasn't been "linked".

If you're not using @angular/cli for building, and you're using webpack, you'll need to make sure the linker is run during packaging, as described in Consuming partial-Ivy code outside the Angular CLI.

Your issue is definitely not related to spectator, it's related to the new angular library package format, which now specifies partial Ivy format for libraries. It's new in ng13 that all the angular libraries are partial Ivy.