Closed dtaylor84 closed 4 years ago
This is an interesting use-case. It seems like what you need here is something from Shallow that can render templates (with optional context) on-demand.
const rendering = await shallow.render();
renderTemplate('#my-template-id', {caption: 'Here is my caption'});
I'm bummed that there is no type-safety here but there isn't any in the Angular implementation either.
Does this solution sound work-able to you? I'm making a few assumptions because I've never used PrimeNG before. Definitely open to suggestions.
Your solution looks reasonable on the face of it -- assuming renderTemplate('[pTemplate="caption"]', ...) would be able to find the ng-templates projected inside the mocked dependency component.
(Drifting off topic, I dug further into ng-mocks and discovered mockDependencyComponent.__render() should work for a dependency with @ContentChild('string-selector'), but doesn't support type based selectors (see here) (nor probably @ContentChildren()) as used by PrimeNG's datatable.
Sorry it's taken so long to get back on this. My day job has been much more demanding since COVID.
I started looking into this and I'm struggling to find a way to render the templates into the mocked-component after the initial render has happened. It seems that the plumbing needs to be setup before the test module is even compiled which means you've really got to have a component that is already setup to render child templates and that component needs to be rendered as part of the test.
I'm trying to look at from a different angle. When I get stuck, I sometimes consider "how would I do this with only TestBed" and I think I would probably try a test-double. Do you think creating a test-double of the dependency-component
would be suitable? It's the same pattern that Angular Router uses for testing routes with the RouterTestingModule
except they provide a whole replacement module. You could probably get away with just one single test-component.
You'd just give the test-double the same selector and override the actual component with shallow with a declare
:
@Component({
selector: 'dependency-component',
template: '...'
})
class DependencyTestComponent {
// some logic that lets me control which child-template to render
}
// ...
shallow.declare(DepencencyTestComponent).dontMock(DependencyTestComponent)
Let me know your thoughts. I'm curious how you would approach this if you were not using shallow-render?
Sorry, I've also been busy!
I think you're right. With TestBed I would have to make a dummy component -- I was just hoping that shallow-render could automagically generate that component for me.
It looks like that could be possible for components with individual @ContentChild() bindings, as ng-mocks has some support for that. But the way the PrimeNG Table does it with @ContentChildren() looks harder (if not impossible) to handle automatically.
For some reason, I was thinking about this while trying to fall asleep last night.
I've not figured out the details, but could you pass some optional configuration to shallow-render (possibly to the render method, alongside the bindings) that would look something like (names need work):
const rendering = shallow.render({ templates: [ { componentSelector: 'table', templateSelectors: ['pTemplate[caption]'] } ]
And with my component-under-test having a template looking like:
<p-table #table>
<ng-template pTemplate="caption">
<div>Hello, World!</div>
</ng-template>
<p-table>
shallow-render could (hopefully) use the configuration to prepare the necessary ViewChild queries to allow something vaguely like (but with a less awful API):
`rendering.renderTemplate('table', 'pTemplate[caption]', context);
I started looking at this today and I'm still not totally sure how it will pan out. Thing is, I think you also have to mock the pTemplate
directive and I don't know how to do that generically.
I'm working on an example that declares mocks manually. If that works out, I'll see if there's any sort of generic-approach to be extracted from it but my guess is that you'll end up needing to create some some helper components in your project somewhere and provide them to your test via something like: .replaceModule(PrimeNgModule, PrimeNgTestingModule)
and manipulate the test versions of the table/template to render what you want.
I looked over at the PrimeNG Repo and it looks like those components are generally free of external dependencies which might make it a good candidate to just use the real-thing in your specs.
I'll maybe have some more time to mess with it later today and will report back.
FYI, I did not get this worked out in a generic way. Unfortunately, I see no way to do this except:
If you have any other ideas or want to give it a go in a PR to prove out an idea we can continue trying. I don't even mind taking a functional POC in a PR and formalizing the code in my free time from there.
Let me know if you want to pick this back up. Gonna close the issue for now.
For anyone using PrimeNG, you can get the pTemplate directive working by providing the SharedModule from PrimeNG.
shallow = new Shallow(SomeComponent, SomeModule)
.provideMock(
ListboxModule,
SharedModule
);
Providing PrimeTemplate directly will not work as it is declared in two separate modules internally in the library.
The component I am trying to test uses a PrimeNG Datatable, which accepts various things as named ng-templates. (e.g. a caption)
I am trying to test that the templates being projected into the mocked dependency are correct, but can't see any way to do that.
Am I missing something, or is this not possible?
I had a look at ng-mocks and noticed it does have an example using MockRender() and MockedComponent<>.__render(), but it is testing the dependency component, not involving the component under test at all...
e.g.
under-test.component.ts
@Input() caption: string;
under-test.component.html
under-test.component.spec.ts