ngParty / ng-metadata

Angular 2 decorators and utils for Angular 1.x
https://hotell.gitbooks.io/ng-metadata/content/
MIT License
355 stars 46 forks source link

[$compile:tpload] Failed to load template #209

Open ptomaszi opened 7 years ago

ptomaszi commented 7 years ago

The component I am testing is using the "templateUrl" to provide a template:

import { Component, Inject, Input, Output, EventEmitter, OnChanges } from 'ng-metadata/core';

@Component({
    selector: 'diContactList',
    templateUrl: 'src/app/portal/parties/contacts/list/contact-list.component.html'
})
export class ContactListComponent implements OnChanges {

Then I have a corresponding unit test:

import { bundle, getInjectableName, NgModule, Component } from 'ng-metadata/core';

describe('ContactListComponent', () => {
    @Component({
        selector: 'test-component',
        template: `
            <di-contact-list>
            </di-contact-list>`
    })
    class TestComponent {
        constructor() {}
    }
    @NgModule({
        declarations: [TestComponent, ContactListComponent]     
    })
    class TestNgModule { } 

    let $compile: ng.ICompileService;
    let $rootScope: ng.IRootScopeService;
    let $scope: ng.IScope;
    let render: IRender<TestComponent>;
    let sut: ContactListComponent;

    beforeEach(() => {
        const TestModule: string = bundle(TestNgModule).name;
        ng.mock.module(TestModule);
    });

    beforeEach(ng.mock.inject(function($injector: ng.auto.IInjectorService) {
        $compile = $injector.get<ng.ICompileService>('$compile');
        $rootScope = $injector.get<ng.IRootScopeService>('$rootScope');
        $scope = $rootScope.$new();

        render = renderFactory($compile, $scope);
        sut = render(ContactListComponent).ctrl;
    }));

When run I am getting the following error:

Error: [$compile:tpload] Failed to load template: src/app/portal/parties/contacts/list/contact-list.component.html (HTTP status: undefined undefined) http://errors.angularjs.org/1.6.5/$compile/tpload?p0=src%2Fapp%2Fportal%2Fparties%2Fcontacts%2Flist%2Fcontact-list.component.html&p1=undefined&p2=undefined

When I remove templateUrl or replace with template in the component, unit tests run correctly.

aciccarello commented 7 years ago

Hello, this shouldn't be any different than testing normal AngularJS components. If you want to test the component template you will need to populate the template cache with the template for that path. Depending on your build system there are several ways you can do that.

https://blog.logentries.com/2015/03/template-caching-in-angular-how-to-avoid-the-storm/

ptomaszi commented 6 years ago

I have found where the issue was. If my component is using the "templateUrl" instead of "template", the render function returns "undefined" for the "ctrl". I have fixed it by modifying "src/testing/utils.js" file and adding "$scope.$digest();" after the $compile:

function renderFactory($compile, $scope) {
    return _compileAndDigest;
    function _compileAndDigest(Directive, _a) {
        var _b = _a === void 0 ? {} : _a, jqHost = _b.jqHost, attrs = _b.attrs, jqChildren = _b.jqChildren;
        var ctrlName = provider_1.getInjectableName(Directive);
        var selector = primitives_1.StringWrapper.kebabCase(ctrlName);
        // is Directive
        if (jqHost) {
            jqHost.attr(selector, '');
        }
        else {
            // is Component
            var hostElement = "<" + selector + "></" + selector + ">";
            jqHost = lang_1.global.angular.element(hostElement);
        }
        // since attributes can be undefined we check them
        if (attrs) {
            jqHost.attr(attrs);
        }
        if (jqChildren) {
            jqHost.append(jqChildren);
        }
        // angular api
        var compiledElement = $compile(jqHost)($scope);
        $scope.$digest();
        var ctrl = compiledElement.controller(ctrlName);
        $scope.$apply();
        return { compiledElement: compiledElement, ctrl: ctrl };
    }
}