Search code examples
angularjsjasminechutzpah

How can I test a directive using templateUrl in Chutzpah's headless browser


Does anyone know how to get a headless browser like Chutzpah's Visual Studio Test Adapter to allow a directive to access its .html template file? Chutzpah uses PhantomJS for a headless browser which appears to limit my options.

I'm using Chutzpah Test Adapter 2.5 and AngularJS 1.2.0-r.2.

I get the error:

Unexpected request: GET myApp/directives/template.html

Which is caused by Angular attempting to use the $http service to access my directive's template.

I've found a few different workarounds:

  1. Manually using XMLHttpRequest to import the templates.
  2. Using a utility like Grunt to inline the template into your directive's JS code.
  3. $httpBackend.when('GET', 'myApp/directives/template.html').passThrough() - this only works in e2e tests, not unit tests.
  4. Put the template directly into the test code.

None of these options particularly satisfy me. I'd prefer to be able to let the directive load its template transparently so I can test it as a component. Is there a way I can get this scenario working?

Example code:

angular.module('myDirective', []).directive('myDirective', function() {
    return {
        restrict: 'E',
        transclude: true,
        templateUrl: 'myApp/directives/template.html',
        // Some other options, omitted for brevity.
    };
});

template.html:

<div><div ng-transclude></div></div>

Example Jasmine test:

describe('myDirective', function() {
    // Assign $scope using inject(), load module etc.
    // Insert workaround for $httpBackend loading my templateUrl here.
    it('should transclude content', function() {
        var element = $compile("<my-directive>Look Mom, I'm in my directive!</my-directive>")($scope);
        $scope.$digest();

        expect(element.text().trim()).toBe("Look Mom, I'm in my directive!");
    });
}

Solution

  • I managed to get it working, using Chutzpah's compile option. I ported karma-ng-html2js-preprocessor into a PowerShell script and then call that from Chutzpah to compile the HTML into JS files.

    Added PowerShell script next to chutzpah.json settings file (the below script contains untested support for PreprendPrefix and StripSuffix)

    # PowerShell port of https://github.com/karma-runner/karma-ng-html2js-preprocessor
    
    Param (
        [parameter(Mandatory=$true)] [string] $Path,
        [string] $Module = '',
        [string] $StripPrefix = '',
        [string] $PrependPrefix = '',
        [string] $StripSuffix = ''
    )
    
    Function EscapeContent($content) {
        $content -replace "\\", "\\\\" -replace "'", "\'" -replace  "`r?`n", "\n' +`n    '"
    }
    
    Function Rename($fileName) {
        "$PrependPrefix" + ("$fileName" -replace "$StripPrefix", '' -replace "$StripSuffix", '' -replace "\\", "/")
    }
    
    $Template = If ("$Module" -eq "") {
        "angular.module('{0}', []).run(function(`$templateCache) {{`n" `
          + "  `$templateCache.put('{0}',`n    '{2}');`n" `
          + "}});`n"
      } Else {
        "(function(module) {{`n" `
            + "try {{`n" `
            + "  module = angular.module('{1}');`n" `
            + "}} catch (e) {{`n" `
            + "  module = angular.module('{1}', []);`n" `
            + "}}`n" `
            + "module.run(function(`$templateCache) {{`n" `
            + "  `$templateCache.put('{0}',`n    '{2}');`n" `
            + "}});`n" `
            + "}})();`n"
      }
    
    Get-ChildItem $Path -Recurse -Filter *.html | Foreach-Object {
        $content = Get-Content $_.FullName | Out-String
        $content = EscapeContent $content
        $fileName = Rename "$($_.FullName)"
        ("$Template" -f "$fileName", "$Module", "$content") | Set-Content "$($_.FullName).js"
    }
    

    Added compile configuration to chutzpah.json and add the "compiled" JS files to References (the reference could added from the test file, but I prefer to use chutzpah.json to manage all the references)

    {
        "References": [
            { "Path": "./path/to/templates/", "Include": "*.html.js" },
        ],
        "Compile": {
            "Extensions": [".html"],
            "ExtensionsWithNoOutput": [".html.js"],
            "SourceDirectory": "./path/to/templates",
            "OutDirectory": "./path/to/templates",
            "Executable": "%powershellexe%",
            "Arguments": "-NoProfile %chutzpahsettingsdir%\\chutzpah-ng-html2js-compiler.ps1 -Path '/path/to/templates/' -Module 'templates' -StripPrefix '.+(?=\\\\templates)'"
        }
    }
    

    In your test files, load the module, in my case they are all in 'templates' module

    beforeEach(module('templates'));
    // or alternatively
    beforeEach(module('/path/to/templates/foo.html'));