Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Absolute or Relative "templateUrls" for angular components

Can someone please help me around this deadend of 1 vs 2?

1) Angular project as AoT in production. This required my source code to be changed to have templateUrl and styleUrls that are relative to the component.ts file.

2) Angular karma as JiT for unit tests. This required my source code to be changed to have templateUrl and styleUrls that are absolute, or the unit test will throw a 404 looking for the .html file.

I would like to avoid maintaining a relative AND an absolute path in the source code.

What are my options here? What is the best practice?

(I'm USING WEBPACK with aot and Karma, NOT angular-cli)

Thanks!!

UPDATE: we're also going to explore converting to angular-cli, but this is a huge project and this is a blocker for us, if anyone knows the trick that angular-cli uses in order to pull this off. or even a work-around so we can get our unit tests back!

I need to know a trick to run Unit tests WITH AoT or at least ALONG SIDE AoT (in other words run aot production and jit unit tests with one set of templateUrls)

Thanks again!

PS.. This worked without AoT perfectly (with the absolute urls), but switching to AoT (with urls relative to the component file) has blocked us.

UPDATE 2: (including all my config files) if I have to create another config file to run tests with (which makes sense) but I'd like to know what 'magic' that test config needs in order to run JiT components, now that my urls are all relative.

webpack.config.js

let webpack = require('webpack');
let path = require('path');
let aot = require('@ngtools/webpack');

module.exports = {
    entry: {
        main: './Angular/main'
    },
    output: {
        path: __dirname,
        filename: './dist/smartcommand-[name].min.js',
        // Lazy load modules by route & chunk
        chunkFilename: './dist/smartcommand-[name].chunk.[hash].min.js'
    },
    resolve: {
        extensions: ['.ts', '.js'],
        modules: [
            path.resolve('./'),
            path.resolve('./node_modules')
        ],
        alias: {
            'ng2-charts/charts/charts': 'node_modules/ng2-charts/bundles/ng2-charts.umd.min.js'
            //'ng2-dragula': 'node_modules/ng2-dragula/bundles/ng2-dragula.umd.min.js'
        }
    },
    module: {
        rules: [
            // Ahead of Time Compilation
            { test: /\.ts$/, loader: '@ngtools/webpack', exclude: [/\.(spec)\.ts$/] },
            // AoT requires all files to be loaded
            { test: /\.html$/, loader: 'html-loader' },
            { test: /\.css$/, loader: 'raw-loader' },
            {
                test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
                loader: 'file-loader', options: { name: '[path][name].[ext]' }
            }
        ]
    },
    plugins: [
        new webpack.optimize.CommonsChunkPlugin({ name: 'main' }),
        // Ahead of Time Plugin
        new aot.AngularCompilerPlugin({
            tsConfigPath: path.resolve('./Angular/tsconfig.json'),
            entryModule: path.resolve('./Angular/_app.module#SmartCommandModule')
            // Use this setting to turn off AoT
            //,skipCodeGeneration: true
        }),
        // Only load the necessary locales for moment
        new webpack.ContextReplacementPlugin(/moment[\/\\]locale$/, /en|es/)
    ],
    // Minimize webpack console output
    stats: { assets: false, children: false }
};

karma.config.js

/* Karma Configuration */
'use strict';
module.exports = function (config) {
    let appBase = 'Angular/'; // transpiled app JS and map files

    config.set({
        plugins: [
            'karma-jasmine',
            'karma-jasmine-html-reporter',
            //'karma-chrome-launcher',
            'karma-firefox-launcher',

            // preprocessors
            'karma-webpack',
            'karma-sourcemap-loader', // show proper (un-bundled) stack traces
            'karma-coverage', // capture unit test code coverage
            'karma-trx-reporter' // report coverage in VSTS
        ],

        // frameworks to use
        // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
        frameworks: ['jasmine'],

        // start these browsers
        // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
        //browsers: ['Chrome'],
        browsers: ['Firefox'],

        // preprocess matching files before serving them to the browser
        // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
        preprocessors: {
            'karma-test-shim.js': ['webpack', 'coverage']
            // for debugging the tests
            //'karma-test-shim.js': ['webpack', 'sourcemap']
        },

        // base path that will be used to resolve all patterns (eg. files, exclude)
        basePath: '',

        // list of files / patterns to load in the browser
        files: [
            // polyfills
            'node_modules/core-js/client/shim.js',

            'Scripts/jquery-3.1.1.min.js',
            'Scripts/jquery-ui-1.12.1.min.js',

            'node_modules/moment/moment.js',
            'node_modules/leaflet/dist/leaflet.js',
            'node_modules/nipplejs/dist/nipplejs.js',

            'node_modules/zone.js/dist/zone.js',
            'node_modules/zone.js/dist/long-stack-trace-zone.js',
            'node_modules/zone.js/dist/proxy.js',
            'node_modules/zone.js/dist/sync-test.js',
            'node_modules/zone.js/dist/jasmine-patch.js',
            'node_modules/zone.js/dist/async-test.js',
            'node_modules/zone.js/dist/fake-async-test.js',

            // rxJS
            { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false },
            { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false },

            // other dependencies
            { pattern: 'node_modules/applicationinsights-js/**/*.js', included: false, watched: false },
            { pattern: 'node_modules/applicationinsights-js/**/*.js.map', included: false, watched: false },

            { pattern: 'node_modules/ng2-charts/**/*.js', included: false, watched: false },
            { pattern: 'node_modules/ng2-charts/**/*.js.map', included: false, watched: false },
            'node_modules/chart.js/dist/Chart.bundle.js',

            { pattern: 'node_modules/primeng/**/*.js', included: false, watched: false },
            { pattern: 'node_modules/ngx-slick/**/*.js', included: false, watched: false },

            { pattern: 'node_modules/ng2-dragula/**/*.js', included: false, watched: false },
            { pattern: 'node_modules/ng2-dragula/**/*.js.map', included: false, watched: false },

            // our SmartCommand bundle
            'dist/smartcommand-main.min.js',

            // our Karma tests
            { pattern: 'karma-test-shim.js', included: true, watched: true },

            // paths for debugging with source maps in dev tools
            { pattern: appBase + '**/*.ts', included: false, watched: false },
            { pattern: appBase + '**/*.js.map', included: false, watched: false },

            // our assets (HTML & CSS) paths loaded via Angular's component compiler
            { pattern: appBase + '**/*.html', included: false, watched: true },
            { pattern: appBase + '**/*.css', included: false, watched: true },
            { pattern: 'Content/*.css', included: true, watched: false },
            { pattern: 'Content/*.css.map', included: false, watched: false },
            { pattern: 'Content/Images/**/*', included: false, watched: false },
            { pattern: 'Content/images/**/*', included: false, watched: false }
        ],

        // proxied base paths for loading assets
        proxies: {
            // required for component assets fetched by Angular's compiler
            '/Angular/': '/base/Angular/',
            '/Content/': '/base/Content/'
        },

        // test results reporter to use
        // available reporters: https://npmjs.org/browse/keyword/karma-reporter
        reporters: ['progress', 'kjhtml', 'coverage', 'trx'],

        // karma code coverage
        coverageReporter: {
            // specify a common output directory 
            dir: 'coverage',
            reporters: [
                // reporters not supporting the `file` property 
                { type: 'html', subdir: 'report-html' },
                { type: 'lcov', subdir: 'report-lcov' },
                // reporters supporting the `file` property, use `subdir` to directly 
                // output them in the `dir` directory 
                { type: 'cobertura', subdir: '.', file: 'cobertura.txt' },
                { type: 'lcovonly', subdir: '.', file: 'report-lcovonly.txt' },
                { type: 'teamcity', subdir: '.', file: 'teamcity.txt' },
                { type: 'text', subdir: '.', file: 'text.txt' },
                { type: 'text-summary', subdir: '.', file: 'text-summary.txt' }
            ]
        },

        // get code coverage in VSTS
        trxReporter: { outputFile: 'test-results.trx', shortTestName: false },

        // web server port
        port: 9876,

        // enable / disable colors in the output (reporters and logs)
        colors: true,

        // level of logging
        // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
        logLevel: config.LOG_INFO,

        // enable / disable watching file and executing tests whenever any file changes
        autoWatch: true,

        // continuous integration mode
        // if true, Karma captures browsers, runs the tests and exits
        singleRun: false,

        // concurrency level
        // how many browsers should be started simultaneous
        concurrency: Infinity,

        client: {
            captureConsole: false, // set to true if you need console output
            builtPaths: [appBase], // add more spec base paths as needed
            clearContext: false // leave Jasmine Spec Runner output visible in browser
        },

        // workaround for disconnects
        browserDisconnectTolerance: 5,
        browserNoActivityTimeout: 50000
    });
};

karma test shim

// 'No stack trace' is usually best for app testing.
Error.stackTraceLimit = 0;
// Uncomment to get full stacktrace output. Sometimes helpful, usually not.
//Error.stackTraceLimit = Infinity;

// Import all .spec.js files in our Angular folder
let appContext = require.context('./Angular', true, /\.spec\.js$/);
appContext.keys().forEach(appContext);

// Start the Test Environment
let testing = require('@angular/core/testing');
let browser = require('@angular/platform-browser-dynamic/testing');
testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());
like image 910
JBoothUA Avatar asked Oct 29 '22 22:10

JBoothUA


1 Answers

Here's the problem I'm seeing. You are in fact using two different Webpack configurations. Obviously, there's your webpack.config.js which is set for AoT.

However, you are using another, different, configuration when you run Karma. The karma-webpack plugin provides the Webpack instance that runs in the Karma process with its own configuration. Unless you yourself write code in your karma.config.js to read webpack.config.js, then Karma will completely ignore that file. So effectively, when you are running Karma, you are running with a different Webpack configuration. The problem is that currently your configuration is empty, which means that Angular is processing templateUrl and styleUrls "raw", and you get the results you are getting.

You need to setup Webpack with a loader that will process the templateUrl and stylesUrls to have Webpack handle them. I would use angular2-template-loader for this.

Looking at your configuration, it seems that by the time Karma sees your files, they've all been transpiled to JavaScript. Normally, angular2-template-loader is used on TypeScript files, but since it does its job using regular expressions, it also works on JavaScript files.

I'd expect adding Webpack configuration with angular2-template-loader to your karma.config.js should work, and adjust for needs specific to your project:

webpack: {
  module: {
    loaders: [
      {
        test: /\.js$/,
        loaders: ['angular2-template-loader'],
        // exclude: [...] You may want to exclude your test files here
        // to save some build time.
      },
      // etc. with raw-loader, html-loader and file-loader as needed.              
    ]},  
  }
like image 125
Louis Avatar answered Nov 11 '22 06:11

Louis