getsaf / shallow-render

Angular testing made easy with shallow rendering and easy mocking. https://getsaf.github.io/shallow-render
MIT License
273 stars 25 forks source link

"No Directive annotation found" error when using custom class decorator on component #208

Closed barbados-clemens closed 3 years ago

barbados-clemens commented 3 years ago

I have a use case where I am performing some overriding logic on component methods, but when trying to test these components via shallow-render the test returns the following handler

When using the TestBed directly it works fine, only shallow-render tests are failing with this error.

Error: No Directive annotation found on syncHandler
        at DirectiveResolver.resolve (webpack:///node_modules/@angular/compiler/fesm2015/compiler.js:20733:1 <- config/test.js:45783:19)
        at Object.outputProxy (webpack:///node_modules/shallow-render/dist/lib/tools/output-proxy.js:23:1 <- config/test.js:538429:94)
        at new Rendering (webpack:///node_modules/shallow-render/dist/lib/models/rendering.js:15:1 <- config/test.js:537297:39)
        at Renderer.<anonymous> (webpack:///node_modules/shallow-render/dist/lib/models/renderer.js:84:1 <- config/test.js:537216:20)
        at Generator.next (<anonymous>)
        at fulfilled (webpack:///node_modules/shallow-render/dist/lib/models/renderer.js:5:1 <- config/test.js:537137:58)
        at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (webpack:///node_modules/zone.js/dist/zone.js:400:1 <- config/test.js:575887:30)
        at ProxyZoneSpec../node_modules/zone.js/dist/zone-testing.js.ProxyZoneSpec.onInvoke (webpack:///node_modules/zone.js/dist/zone-testing.js:301:1 <- config/test.js:573582:43)
        at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (webpack:///node_modules/zone.js/dist/zone.js:399:1 <- config/test.js:575886:56)
        at Zone../node_modules/zone.js/dist/zone.js.Zone.run (webpack:///node_modules/zone.js/dist/zone.js:160:1 <- config/test.js:575647:47)

where syncHandler is the method that is overriding the method on a component class.

when debugging I don't see any extra annotation data on the original method so I'm not 100% sure what is missing. I don't remove any of the class-level annotations that I can tell.

Here is a minimal reproducible https://github.com/barbados-clemens/ng-shallow-render-decorator-error

getsaf commented 3 years ago

Thanks for the detail and the reproduction. I pulled down your project and inspected the decorator. It seems to be an issue with your decorator replacing the component's constructor method which does not copy over the constructor's reflection metadata to the replacement.

I was able to resolve this by changing this line:

const isMethod = descriptor.value instanceof Function;

to avoid replacing the constructor:

const isMethod = descriptor.value instanceof Function && descriptor !== target.prototype.constructor;

You may have some luck in attempting to copy the reflected metadata from the constructor but this could get tricky since Angular has changed the way this works over the years and you'd potentially run into issues if you decorated an "older" metadata constructor.

barbados-clemens commented 3 years ago

Thank you for the help!

the code provided had to me slightly modified to be

const isMethod = descriptor.value instanceof Function && descriptor.value !== target.prototype.constructor;

but that did the trick and makes total sense. Thank you for the quick response!

getsaf commented 3 years ago

Awesome! Thanks for submitting the issue with so much detail, that really made debugging super-easy.