2
votes

I've been struggling for days now with the fact I cannot run any real test with Karma. I can run tests which do not require imports (like basic sanity tests) but as soon I have to import something from my app I get the error:

system.src.js:1085 GET http://localhost:9876/base/dist/components/test.service 404 (Not Found)fetchTextFromURL @ system.src.js:1085(anonymous function) @ system.src.js:1646ZoneAwarePromise @ angular2-polyfills.js:589(anonymous function) @ system.src.js:1645(anonymous function) @ system.src.js:2667(anonymous function) @ system.src.js:3239(anonymous function) @ system.src.js:3506(anonymous function) @ system.src.js:3888(anonymous function) @ system.src.js:4347(anonymous function) @ system.src.js:4599(anonymous function) @ system.src.js:337ZoneDelegate.invoke @ angular2-polyfills.js:332Zone.run @ angular2-polyfills.js:227(anonymous function) @ angular2-polyfills.js:576ZoneDelegate.invokeTask @ angular2-polyfills.js:365Zone.runTask @ angular2-polyfills.js:263drainMicroTaskQueue @ angular2-polyfills.js:482ZoneTask.invoke @ angular2-polyfills.js:434 angular2-polyfills.js:469 Unhandled Promise rejection: karma.error is not a function ; Zone: ; Task: Promise.then ; Value: TypeError: karma.error is not a function(…)consoleError @ angular2-polyfills.js:469drainMicroTaskQueue @ angular2-polyfills.js:498ZoneTask.invoke @ angular2-polyfills.js:434 angular2-polyfills.js:471 Error: Uncaught (in promise): TypeError: karma.error is not a function(…)

I need to mention : Example test:

import { provide } from 'angular2/core';
import {TestService} from './test.service';
import {
//   beforeEach,
  beforeEachProviders,
  describe,
  expect,
  it,
  inject,
//   injectAsync
} from 'angular2/testing';

 describe('TestService', () => {

  beforeEachProviders(() => [
    provide(TestService, {useClass: TestService})
  ]);
  it('should say hello with name', inject([TestService], (testService: TestService) => {
    expect(testService.name).toBe('Injected Service');
  }));

  it('should say hello with name', () => {
    expect(true).toBe(true);
  });

});

My project structure Is multi module based like :

enter image description here

Karma.conf.js:

frameworks: ['jasmine'],

files: [
    // paths loaded by Karma
    { pattern: 'node_modules/angular2/bundles/angular2-polyfills.js', included: true, watched: true },
    { pattern: 'node_modules/systemjs/dist/system.src.js', included: true, watched: true },
    { pattern: 'node_modules/rxjs/bundles/rx.js', included: true, watched: true },
    { pattern: 'node_modules/angular2/bundles/angular2.js', included: true, watched: true },
    { pattern: 'node_modules/angular2/bundles/testing.dev.js', included: true, watched: true },
    { pattern: 'karma-test-shim.js', included: true, watched: true },
    { pattern: 'dist/components/matchers.js', included: true, watched: true },

    // paths loaded via module imports
    { pattern: 'dist/components/**/*.js', included: false, watched: true },

    // paths loaded via Angular's component compiler
    // (these paths need to be rewritten, see proxies section)
    { pattern: 'dist/*.html', included: false, watched: true },
    { pattern: 'dist/styles/*.css', included: false, watched: true },
    { pattern: 'dist/components/**/*.html', included: false, watched: true },
    { pattern: 'dist/components/**/*.css', included: false, watched: true },

    // paths to support debugging with source maps in dev tools
    { pattern: 'src/components/**/*.ts', included: false, watched: false },
    { pattern: 'dist/components/**/*.js.map', included: false, watched: false }
],

// proxied base paths
proxies: {
    // required for component assests fetched by Angular's compiler
    "/src/": "/base/src"
}, 

karma-test-shim.js:

...

System.config({
    packages: {
        'base/src': {
            defaultExtension: false,
            format: 'register',
            map: Object.keys(window.__karma__.files).
                filter(onlyAppFiles).
                reduce(function createPathRecords(pathsMapping, appPath) {
                    // creates local module name mapping to global path with karma's fingerprint in path, e.g.:
                    // './hero.service': '/base/src/app/hero.service.js?f4523daf879cfb7310ef6242682ccf10b2041b3e'
                    var moduleName = appPath.replace(/^\/base\/src\//, './').replace(/\.js$/, '');
                    pathsMapping[moduleName] = appPath + '?' + window.__karma__.files[appPath]
                    return pathsMapping;
                }, {})

        }
    }
});


function filePath2moduleName(filePath) {
    console.log('filePath2moduleName', filePath)
    return filePath.
        replace(/^\//, '').              // remove / prefix
        replace(/\.\w+$/, '');           // remove suffix
}


function onlyAppFiles(filePath) {
    console.log('filePath', filePath)
    return /^\/base\/src\/.*\.js$/.test(filePath)
}


function onlySpecFiles(path) {
    return /.spec\.js$/.test(path);
}

Any idea very welcomed !

1
And your ts files are compiled into the dist folder? Right?Thierry Templier

1 Answers

1
votes

If you compile your TS files into the dist folder, I think that you have a gap. I would try the following within your karma-test-shim.js file:

System.config({
  packages: {
    'base/dist': {
        defaultExtension: false,
        format: 'cjs',
        map: Object.keys(window.__karma__.files).filter(onlyAppFiles).reduce(createPathRecords, {})
    }
  }
});

(...)

function createPathRecords(pathsMapping, appPath) {
  var pathParts = appPath.split('/');
  var moduleName = './' + pathParts.slice(Math.max(pathParts.length - 2, 1)).join('/');
  moduleName = moduleName.replace(/\.js$/, '');
  pathsMapping[moduleName] = appPath + '?' + window.__karma__.files[appPath];
  return pathsMapping;
}

The following line in your createPathRecords function seems strange since you shouldn't have files from src but from dist.

With this code, I have the following SystemJS configuration generated (map block):

System.config({
  packages: {
    'base/dist': {
      defaultExtension: false,
      format: 'register',
      map: {
        './comps/my-list': '/base/dist/comps/my-list.js?e9(...)',
        './dist/my-app': '/base/dist/my-app.js?fe(...)',
        './pipes/my-pipe': '/base/dist/pipes/my-pipe.js?78(...)',
        './services/http-service': '/base/dist/services/http-service.js?c1(...)',
        './services/my-service': '/base/dist/services/my-service.js?b1(...)'
      }
    }
  }
});

In your case /base/dist/components/test.service can't be resolved by SystemJS so I guess that either your map block isn't correct or the entry for the packages one.

Notice that the entry of the map block are relative to the key of the packages one. In your case, I guess that you generate the following configuration:

System.config({
  packages: {
    'base/src': {
      defaultExtension: false,
      format: 'register',
      map: {
        './components/test.service': '/base/dist/components/test.service.js',
        (...)
      }
    }
  }
});

So to debug your problem, I would do the following:

  • Logging the appPath within your createPathRecords function:

    reduce(function createPathRecords(pathsMapping, appPath) {
      console.log('appPath = '+appPath);
      (...)
    }
    
  • Logging the whole map configuration:

    console.log(Object.keys(window.__karma__.files).filter(onlyAppFiles).reduce(createPathRecords, {}));
    

Based on these hints, you need to adapt your code to have this configuration:

{
  './components/test.service': '/base/dist/components/test.service.js',
  (...)
}