ben4ever ben4ever - 3 years ago 174
Dart Question

Trying to use `material-dropdown-select` of `angular_components` in component tests

I'm doing some tests with the latest

angular_components-0.6.0-alpha+2
package since I want to unit test my
<material-dropdown-select>
component in my Angular app. I basically want to use the
pageloader
package to click and open the dropdown select during a test to click on an option.

Now I'm trying to mount the
<div id="default-acx-overlay-container">
(which gets generated by
angular_components
) within my custom
<div id="my-container">


<my-app>Loading...</my-app>
<div id="my-container"></div>
</body>
</html>


since
<div id="default-acx-overlay-container">
would otherwise by default be a direct child of
<body>
(where I wouldn't be able to access it with
pageloader
's
@ByCSS
selector). I therefore override the
overlayContainerParent
Angular provider in order to change this (note the
providers:
part):

HtmlElement getOverlayContainerParent(Document doc) =>
doc.querySelector('#my-container');

@Component(
selector: 'material-select-demo',
styleUrls: const ['material_select_demo.css'],
templateUrl: 'material_select_demo.html',
directives: const [
CORE_DIRECTIVES,
DisplayNameRendererDirective,
ExampleRendererComponent,
MaterialCheckboxComponent,
MaterialDropdownSelectComponent,
MaterialSelectComponent,
MaterialSelectItemComponent,
],
providers: const [
const Provider(overlayContainerParent,
useFactory: getOverlayContainerParent, deps: const [Document]),
],
)
class MaterialSelectDemoComponent {


My problem however is that for some reason the provider doesn't get overridden.

I created an example app which is a simplified version of the
angular_components_example
app to demonstrate my problem. If you run the app, then open the dropdown select, and then inspect the DOM you will see that
angular_components_example
's
<div id="default-acx-overlay-container">
does not get mounted under my
<div id="my-container">
as I intend to. Here is my example app: ben4ever/angular_components_example

Answer Source

You are very close. We do something very similar with specifying a different overlayContainerParent the problem is the top level injectable isn't overlayContainerParent. The dependency tree looks like this: OverlayService -> OverlayDomRenderService -> overlayContainerToken -> overlayContainerParent

The way angular injection works is when the an instance of OverlayService is asked for it will walk up the injection tree till it finds that binding. Then at that level in the tree it will get it's dependencies at the same level and above. Thus in this case you are specifying a different overlayContainerParent implementation lower in the injection tree so it will never be used unless you are asking for that token to be injected directly.

In our tests our providers look something like this:

const [popupBindings, const Provider(overlayContainerParent, useFactory: getOverlayContainerParent, deps: const [Document])

This will ensure that the overlay bindings are specified at the same level in the tree and your value will override the one in popupBindings.

Note if you put this on a component directly you are going to get a logging error about having nested overlay bindings. This was because engineers were specifying popup bindings on the component directly and not being able to test with a different container because they couldn't override the value in tests (The non-testing bindings were always lower in the injection tree than anywhere we could provider testing bindings.)

The better solution is to use NgTestBed from angular_test and specify these providers at the top level using addProviders.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download