sherweb / ngx-materialize

Angular wrap around Materialize library
https://sherweb.github.io/ngx-materialize/
Apache License 2.0
315 stars 75 forks source link

Sidenav, unit test : Cannot read property 'classList' of undefined #169

Closed chych closed 7 years ago

chych commented 7 years ago

Hello,

my project is under angular4 and i used angular-cli to run the test (ng test)

I used the mz-sidenav component on my code without change anything for the moment (https://sherweb.github.io/ng2-materialize/sidenav) and when i tried to run unit test i had this error :

TypeError: Cannot read property 'classList' of undefined at EmulatedEncapsulationDomRenderer2.webpackJsonp../node_modules/@angular/platform-browser/@angular/platform-browser.es5.js.DefaultDomRenderer2.addClass (http://localhost:9876/_karma_webpack_/vendor.bundle.js:57837:70) at DebugRenderer2.webpackJsonp../node_modules/@angular/core/@angular/core.es5.js.DebugRenderer2.addClass (http://localhost:9876/_karma_webpack_/vendor.bundle.js:44310:23) at RendererAdapter.webpackJsonp../node_modules/@angular/core/@angular/core.es5.js.RendererAdapter.setElementClass (http://localhost:9876/_karma_webpack_/vendor.bundle.js:41533:27) at MzSidenavComponent.webpackJsonp../node_modules/ng2-materialize/dist/sidenav/sidenav.component.js.MzSidenavComponent.initCollapseButton (http://localhost:9876/_karma_webpack_/vendor.bundle.js:69355:27) at MzSidenavComponent.webpackJsonp../node_modules/ng2-materialize/dist/sidenav/sidenav.component.js.MzSidenavComponent.ngAfterViewInit (http://localhost:9876/_karma_webpack_/vendor.bundle.js:69340:14) at callProviderLifecycles (http://localhost:9876/_karma_webpack_/vendor.bundle.js:42156:18) at callElementProvidersLifecycles (http://localhost:9876/_karma_webpack_/vendor.bundle.js:42131:13) at callLifecycleHooksChildrenFirst (http://localhost:9876/_karma_webpack_/vendor.bundle.js:42115:17) at checkAndUpdateView (http://localhost:9876/_karma_webpack_/vendor.bundle.js:43133:5) at callViewAction (http://localhost:9876/_karma_webpack_/vendor.bundle.js:43440:17) at execComponentViewsAction (http://localhost:9876/_karma_webpack_/vendor.bundle.js:43386:13) at checkAndUpdateView (http://localhost:9876/_karma_webpack_/vendor.bundle.js:43131:5) at callWithDebugContext (http://localhost:9876/_karma_webpack_/vendor.bundle.js:44113:42) at Object.debugCheckAndUpdateView [as checkAndUpdateView] (http://localhost:9876/_karma_webpack_/vendor.bundle.js:43653:12) at ViewRef_.webpackJsonp../node_modules/@angular/core/@angular/core.es5.js.ViewRef_.detectChanges (http://localhost:9876/_karma_webpack_/vendor.bundle.js:41223:63)

jfcere commented 7 years ago

Hi @chych, can you provide a sample of your html, component and test code? I'm wondering if the error is related to your test code or to our library ...

chych commented 7 years ago

Actually my component is empty, no code except constructor..., html template just content mz-sidenav component the problem appear with the [collapseButtonId]="'btnSidenav'" because i split the header and sidenav in two differents component.

My home component contains :


<app-header></app-header>
<app-home-sidenav></app-home-sidenav>

My header template:

<header>
    <div class="navbar-fixed navbar-mycj">
        <nav class="nav-mycj blue-grey" [ngClass]="{navFixed: navIsFixed}" role="navigation">
            <div class="nav-wrapper container">
                <a id="logo-container" href="#" class="brand-logo position">
                    <img alt="logo mycloudjob" src="./assets/images/mycj/logo-navbar.png">
                </a>
                <ul class="right hide-on-med-and-down">
                    <li><a href="#" class="btn blue-grey">Se connecter</a></li>
                    <li><a href="#" class="btn pink">S'inscrire</a></li>
                </ul>

                <a href="#"
                   [id]="'btnSidenav'"
                   class="button-collapse hide-on-large-only right"><i class="material-icons">menu</i></a>

            </div>
        </nav>
    </div>
</header>

My component containing(app-home-sidenav) :

import {Component, OnInit} from "@angular/core";

@Component({
    selector: 'app-home-sidenav',
    templateUrl: './home-sidenav.component.html',
    styleUrls: ['./home-sidenav.component.css']
})
export class HomeSidenavComponent implements OnInit {

    constructor() { }

    ngOnInit() {
    }

}

My component html(app-home-sidenav) :

<mz-sidenav
    [id]="'sidenav'"
    [collapseButtonId]="'btnSidenav'">
    <mz-sidenav-header>
        Sidenav header
    </mz-sidenav-header>
    <mz-sidenav-subheader>Link Subheader</mz-sidenav-subheader>
    <mz-sidenav-link>
        <a href="#">First Link</a>
    </mz-sidenav-link>
    <!--<mz-sidenav-divider></mz-sidenav-divider>-->
    <mz-sidenav-link [active]="true">
        <a href="#">Active Link</a>
    </mz-sidenav-link>
    <mz-sidenav-divider></mz-sidenav-divider>
    <mz-sidenav-link>
        <a href="#" class="waves-effect">Second Link With Waves</a>
    </mz-sidenav-link>
</mz-sidenav>

My component test(app-home-sidenav)

import {async, ComponentFixture, TestBed} from "@angular/core/testing";
import {HomeSidenavComponent} from "./home-sidenav.component";
import {MaterializeModule} from "ng2-materialize";

describe('HomeSidenavComponent', () => {
    let component: HomeSidenavComponent;
    let fixture: ComponentFixture<HomeSidenavComponent>;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [
                HomeSidenavComponent,
            ],
            imports: [
                MaterializeModule,
            ],
        })
            .compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(HomeSidenavComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();

    });

    it('should be created', () => {
        expect(component).toBeTruthy();
    });
});
chych commented 7 years ago

The solution i found it's to not separate my colapse button and the sidenav :

<header>
    <div class="navbar-fixed navbar-mycj">
        <nav class="nav-mycj blue-grey" [ngClass]="{navFixed: navIsFixed}" role="navigation">
            <div class="nav-wrapper container">
                <a id="logo-container" href="#" class="brand-logo position">
                    <img alt="logo mycloudjob" src="./assets/images/mycj/logo-navbar.png">
                </a>
                <ul class="right hide-on-med-and-down">
                    <li><a href="" class="btn blue-grey">Se connecter</a></li>
                    <li><a href="" class="btn pink">S'inscrire</a></li>
                </ul>

                <a href=""
                   [id]="'btnSidenav'"
                   class="button-collapse hide-on-large-only right"><i class="material-icons">menu</i></a>

            </div>
        </nav>
    </div>
</header>

<mz-sidenav
    [id]="'sidenav'"
    [collapseButtonId]="'btnSidenav'">
    <mz-sidenav-header>
        Sidenav header
    </mz-sidenav-header>
    <mz-sidenav-subheader>Link Subheader</mz-sidenav-subheader>
    <mz-sidenav-link>
        <a href="#">First Link</a>
    </mz-sidenav-link>
    <!--<mz-sidenav-divider></mz-sidenav-divider>-->
    <mz-sidenav-link [active]="true">
        <a href="#">Active Link</a>
    </mz-sidenav-link>
    <mz-sidenav-divider></mz-sidenav-divider>
    <mz-sidenav-link>
        <a href="#" class="waves-effect">Second Link With Waves</a>
    </mz-sidenav-link>
</mz-sidenav>
jfcere commented 7 years ago

@chych the reason why your test fails is because it cannot find the provided collapseButtonId as it does not exist in the context of your test which only include the app-home-sidenav component.

If you absolutely want to keep them seperated you will need to create a fake collapse button element with the exact same button id in your app-home-sidenav test file.

function createCollapseButton(id: string): void {
  const collapseButton = document.createElement('a');
  collapseButton.setAttribute('id', id);
  document.body.appendChild(collapseButton);
}

beforeEach(() => {
  fixture = TestBed.createComponent(HomeSidenavComponent);
  component = fixture.componentInstance;
  // create fake collapse button in DOM before triggering 
  // change detection that initialize the component
  createCollapseButton('btnSidenav');
  fixture.detectChanges();
});

Although this would work I don't suggest you to seperate the collapse button and the sidenav because your test won't assure you that the button is really there at runtime or is really having the right id.

chych commented 7 years ago

I'm agree with you it's the solution that i adopted