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

Issue

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-archive/angular_components_example

Solution

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.

Answered By – Ted Sander

Answer Checked By – Timothy Miller (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *