2016-04-16 15 views
2

Ich schreibe eine App mit react/redux/webpack. Ich baue meine Tests mit Karma, Mokka aus und möchte Istanbul für die Testabdeckung verwenden. In einem Versuch, Abdeckung zu erhalten mit Karma-Berichterstattung zu arbeiten ich habe folgende karma.config.jsTestabdeckung React, Istanbul -_registerComponent (...): Zielcontainer ist kein DOM-Element

var argv = require('yargs').argv; 
var path = require('path'); 
var webpack = require('webpack'); 

const PATHS = { 
    test: path.join(__dirname, 'test'), 
    app: path.join(__dirname, 'app'), 
} 

module.exports = function(config) { 
    config.set({ 
    // only use PhantomJS for our 'test' browser 
    browsers: ['PhantomJS'], 

    // just run once by default unless --watch flag is passed 
    singleRun: !argv.watch, 

    // which karma frameworks do we want integrated 
    frameworks: ['mocha', 'chai'], 

    // include some polyfills for babel and phantomjs 
    files: [ 
     'node_modules/babel-polyfill/dist/polyfill.js', 
     './node_modules/phantomjs-polyfill/bind-polyfill.js', 
     // './test/**/*.js', // specify files to watch for tests, 
     'test/index.js', 
    ], 

    preprocessors: { 
     // these files we want to be precompiled with webpack 
     // also run tests through sourcemap for easier debugging 
     // 'test/*.spec.js': ['webpack'], 
     'test/index.js': ['webpack', 'sourcemap'] 
    }, 

    // A lot of people will reuse the same webpack config that they use 
    // in development for karma but remove any production plugins like UglifyJS etc. 
    // I chose to just re-write the config so readers can see what it needs to have 
    webpack: { 
     devtool: 'inline-source-map', 
     resolve: { 
     // allow us to import components in tests like: 
     // import Example from 'components/Example'; 
     root: PATHS.app, 

     // allow us to avoid including extension name 
     extensions: ['', '.js', '.jsx'], 

     // required for enzyme to work properly 
     alias: { 
      'sinon': 'sinon/pkg/sinon' 
     } 
     }, 
     module: { 
     // don't run babel-loader through the sinon module 
     noParse: [ 
      /node_modules\/sinon\// 
     ], 
     preLoaders: [ 
      // instrument only testing sources with Istanbul 
      // { 
      //  test: /\.js$/, 
      //  include: path.resolve('app/'), 
      //  exclude: /node_modules/, 
      //  loader: 'istanbul-instrumenter' 
      // } 
      { 
       test: /\.jsx?$/, 
       exclude: [/node_modules/, /test/], 
       loader: 'isparta-instrumenter-loader' 
      }, 
     ], 

     // run babel loader for our tests 
     loaders: [ 
      { 
      test: /\.css$/, 
      loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]' 
      }, 
      { 
      test: /\.jsx?$/, 
      loader: 'babel', 
      exclude: /node_modules/, 
      query: { 
       presets: ['es2015', 'react', 'survivejs-kanban'] 
      } 
      }, 
     ], 
     }, 
     // required for enzyme to work properly 
     externals: { 
     'jsdom': 'window', 
     'cheerio': 'window', 
     'react/lib/ExecutionEnvironment': true, 
     'react/lib/ReactContext': 'window' 
     }, 
    }, 
    // displays tests in a nice readable format 
    reporters: ['spec', 'coverage'], 
    webpackMiddleware: { 
     noInfo: true 
    }, 
    // tell karma all the plugins we're going to be using to prevent warnings 
    plugins: [ 
     'karma-mocha', 
     'karma-chai', 
     'karma-webpack', 
     'karma-phantomjs-launcher', 
     'karma-spec-reporter', 
     'karma-sourcemap-loader', 
     'karma-coverage' 
    ] 
    }); 
}; 

Karma Einstiegspunkt einzurichten, ist nur test/index.js. Das sieht so aus

// require all the tests so they will run. 
const testsContext = require.context('.', true, /spec/); 
testsContext.keys().forEach(testsContext); 

// require all the .js and .jsx files in app so they will be included in coverage 
const componentsContext = require.context('../app/', true, /jsx?$/); 

// Date: April 16 2016 
// Author: Benjamin Conant 
// componentsContext.keys() is an array that includes file paths for all the 
// .js and .jsx files in ./app .... karma fails with 
// PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR 
// Invariant Violation: _registerComponent(...): Target container is not a DOM element. 
// at /Users/benconant/Dev/MyFin/my-fin-front-end/test/index.js:15283 <- webpack:///~/react/~/fbjs/lib/invariant.js:45:0 
// if the entry point index.jsx file is included. Seems to have somthing 
// to do with trying to actually write to the DOM. So, I filter out index.jsx and the tests run very well. 
// This means that we will probubly not be able to test index.jsx until this is solved. 

let componentsContextKeysWithoutIndexJsx = componentsContext.keys().filter(function (filePath) { return filePath !== './index.jsx' }); 
componentsContextKeysWithoutIndexJsx.forEach(componentsContext); 
// componentsContext.keys().forEach(componentsContext); --- the way it should be if we did not have to remove ./index.jsx 

Wie Sie aus dem datierten Kommentar sehen können. Wenn index.jsx enthalten ist, wenn ich die Tests laufen bekomme ich ...

PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR 
    Invariant Violation: _registerComponent(...): Target container is not a DOM element. 
    at /Users/benconant/Dev/MyFin/my-fin-front-end/test/index.js:15283 <- webpack:///~/react/~/fbjs/lib/invariant.js:45:0 

hier mein ist index.jsx Referenz

import React from 'react'; 
import ReactDOM from 'react-dom'; 
import { Provider } from 'react-redux';; 
import { createStore, combineReducers, applyMiddleware } from 'redux'; 
import { Router, Route, Link, browserHistory, hashHistory, IndexRoute } from 'react-router'; 
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'; 

import injectTapEventPlugin from 'react-tap-event-plugin'; 
injectTapEventPlugin(); 


import configureStore from './store/configureStore'; 
import todoApp from './reducers'; 
import App from './containers/app/App'; 
import IntroSlides from './containers/IntroSlides'; 
import LandingPage from './containers/LandingPage'; 

let store = configureStore(); 

const history = process.env.HASH_ROUTING ? syncHistoryWithStore(hashHistory, store) : syncHistoryWithStore(browserHistory, store); 


ReactDOM.render(
    <Provider store={store}> 
    <Router history={history}> 
     <Route path="/" component={App}> 
     <IndexRoute component={LandingPage} /> 
     <Route path="intro" component={IntroSlides}/> 
     </Route> 
    </Router> 
    </Provider>, 
    document.getElementById('app') 
) 

Ich bin etwa eine Woche in das Ökosystem reagieren und so sind fast sicher tun etwas dummes aber das hat viele Stunden in Anspruch genommen und Hilfe würde sehr geschätzt!

+0

Diese Frage ist sehr verwandt http://StackOverflow.com/Questions/30751385/Karma-Coverage-and-Babelbrowserify-preprocessing –

Antwort

1

Ich hatte das gleiche Problem, das in meinem Fall aufgetreten ist, weil React das Element nicht finden konnte, in dem es das html rendern musste.

fand ich eine schnelle Lösung durch die folgenden if-Anweisung in meine Haupt-js-Datei hinzufügen:

if ($('#app').length <= 0) { 
    $('body').prepend('<div id="app"></div>'); 
} 

ReactDom.render(
    <App />, 
    document.getElementById('app') 
); 

Ich bin mir bewusst, dies nicht der beste Weg, um es zu fixieren sein müssen, aber zumindest funktioniert es jetzt . Wenn jemand einen besseren Weg kennt, lass es uns wissen!

Ich habe diese Lösung auch auf den Thread gepostet, den Sie in Ihrem Kommentar erwähnt haben.