配置Karma以使用requirejs

时间:2015-11-24 13:46:30

标签: javascript node.js requirejs karma-runner pegjs

尝试使用PegJS和requirejs测试项目。 我有几个源文件,实现为AMD模块(定义),通过require API加载。在目录结构下面:

js/
   somefile.js
   main.js
   parser.js
test/
   parser.spec.js

我编写了一个 parser.js 模块来加载PegJS语法文件并使用PegJS创建一个peg解析器:

define(function() {
  'use strict';

  var PEG = require('pegjs');
  var grammarFile = 'grammar.peg'

return {
  parse: function(fs, content, debug) {
    var grammar = fs.readFileSync(grammarFile, 'utf8').toString();
    // Build parser from grammar
    var parser = PEG.buildParser(grammar, { trace: debug });
    [...]

使用带有节点的命令行执行的main.js可以正常工作。 现在我想用karma,jasmine和PhantomJS来测试我的项目。我有一个像这样的 karma.conf.js

frameworks: ['jasmine', 'requirejs'],
files: [
  { pattern: './test/**/*.spec.js', included: false },
  { pattern: './js/**/*.js', included: false},
  './test/test-main.js',
],

还有一个名为 test-main.js 的require bootstrap文件,它以这种方式配置:

'use strict';

var allTestFiles = [];
var TEST_REGEXP = /(spec|test)\.js$/i;

// Get a list of all the test files to include
Object.keys(window.__karma__.files).forEach(function(file) {
  console.log(file);
  if (TEST_REGEXP.test(file)) {
    // Normalize paths to RequireJS module names.
    // If you require sub-dependencies of test files to be loaded as-is (requiring file extension)
    // then do not normalize the paths
    var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
    allTestFiles.push(file);
  }
});

require.config({
  // Karma serves files under /base, which is the basePath from your config file
  baseUrl: '/base/js',
  // dynamically load all test files
  deps: allTestFiles,
  // we have to kickoff jasmine, as it is asynchronous
  callback: window.__karma__.start
});

现在,当我启动测试(grunt karma)时,我收到了这个错误:

PhantomJS 1.9.8 (Linux 0.0.0) ERROR: Error{message: 'Module name "pegjs" has not been loaded yet for context: _. Use require([])

所以我尝试将这些方式中的pegjs包含在Karma加载的文件中 karma.conf.js

files: [
  { pattern: 'node_modules/pegjs/lib/**/*.js', included: true  },
  { pattern: './test/**/*.spec.js', included: false },
  { pattern: './js/**/*.js', included: false},
  './test/test-main.js'
],

当我这样做时,我仍然会收到错误:

Error: Module name "utils/arrays" has not been loaded yet for context: _. Use require([])

查看pegjs模块,确实有一个arrays.js文件:

compiler/
compiler.js
grammar-error.js
parser.js
peg.js
utils/
  arrays.js
  classes.js
  objects.js

所以试图也包括数组:

files: [
  { pattern: 'node_modules/pegjs/lib/utils/arrays.js', included: true },
  { pattern: 'node_modules/pegjs/lib/**/*.js', included: true  },
  { pattern: './test/**/*.spec.js', included: false },
  { pattern: './js/**/*.js', included: false},
  './test/test-main.js'
],

我明白了:

ReferenceError: Can't find variable: module
at /blabla/node_modules/pegjs/lib/utils/arrays.js:108

因为:

108 module.exports = arrays;

因此,在加载npm模块的过程中,我试图以这种方式加载bower模块:

files: [
  { pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
  { pattern: './test/**/*.spec.js', included: false },
  { pattern: './js/**/*.js', included: false},
  './test/test-main.js'
],

然后你再来一次:

PhantomJS 1.9.8 (Linux 0.0.0) ERROR: Error{message: 'Module name "pegjs" has not been loaded yet for context: _. Use require([])

还尝试不在生成业力的网页中包含pegjs:

files: [
  { pattern: 'bower_components/pegjs/peg-0.9.0.js', included: false },
  { pattern: './test/**/*.spec.js', included: false },
  { pattern: './js/**/*.js', included: false},
  './test/test-main.js'
],

但它失败了:

PhantomJS 1.9.8 (Linux 0.0.0) ERROR: 'There is no timestamp for /base/bower_components/pegjs/peg-0.9.0!'

试图将bower_component文件夹放在js文件夹中,但没有运气。

所以我不知道从这里出发......在谷歌或这里找不到相关内容。这似乎是一个特殊的问题,要求js / pegjs与业力......任何想法都是受欢迎的。

更新丹德的回答:

所以我在 parser.js 中从同步需求切换到异步需求:

define(['../bower_components/pegjs/peg-0.9.0'], function(PEG) {
  'use strict';

  var grammarFile = 'grammar.peg'

return {
  parse: function(fs, content, debug) {
    var grammar = fs.readFileSync(grammarFile, 'utf8').toString();
    // Build parser from grammar
    var parser = PEG.buildParser(grammar, { trace: debug });
    [...]

试图在 karma.conf.js 中加入pegjs bower组件:

{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: false },

或不包含它:

{ pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },

但总是得到同样的错误:

Error: Script error for "/blabla/bower_components/pegjs/peg-0.9.0", needed by: /blabla/js/parser.js
http://requirejs.org/docs/errors.html#scripterror
at /blabla/node_modules/requirejs/require.js:140

是文件存在:

$ file /home/aa024149/share/logofrjs/bower_components/pegjs/peg-0.9.0.js 
/blabla/bower_components/pegjs/peg-0.9.0.js: ASCII text, with very long lines

UPDATE2 :最后了解并找到了可接受的解决方案。

3 个答案:

答案 0 :(得分:3)

听起来你正在通过requirejs加载pegjs。如果是这种情况,pegjs不应该是包含的文件。在您的karma.conf.js中,您是否尝试过以下操作:

files: [
  { pattern: 'bower_components/pegjs/peg-0.9.0.js', included: false },
  { pattern: './test/**/*.spec.js', included: false },
  { pattern: './js/**/*.js', included: false},
  './test/test-main.js'
],

包含的值表示业力服务器生成的网页是否应该具有该文件的脚本标记(请参阅http://karma-runner.github.io/0.13/config/files.html)。所以你的karma.config:

files: [
  { pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
  { pattern: './test/**/*.spec.js', included: false },
  { pattern: './js/**/*.js', included: false},
  './test/test-main.js'
],

将导致业力生成一个头标记类似于:

的网页
<head>
  <script src="/base/bower_components/pegjs/peg-0.9.0.js"></script>
  <script src="/base/require.js"></script>
  <script src="/base/test/test-main.js"></script>
</head>

根据我的经验,我看到很多与此类似的行为是由于我的文件标记为included: true而造成的。如果您尝试使用requirejs加载文件,请确保将其标记为included: false

我认为这标志着它是一个预处理工作,但我不完全确定为什么会产生这样的差异。

答案 1 :(得分:1)

据我所知,Karma是一个测试框架,它将在浏览器中运行您的测试。

这不适合测试许多节点模块。

浏览器无法同步执行此操作:var PEG = require('pegjs')。这就是为什么要求你使用require([])来传递回调,以便在pegjs加载完成时执行。

使用pegjs的bower版本并确保在调用require('pegjs')之前加载它可能会有所帮助。这将确保pegjs已经加载上下文_(默认的requirejs上下文,我猜)。

它也无法使用fs.readFileSync(grammarFile, 'utf8')从文件系统加载文件,因此您必须以另一种方式执行此操作。您可以通过将其置于文件数组中然后使用requirejs text plugin加载它来请求Karma托管您的peg语法。

如果您正在测试的模块的目标是在node.js上而不是在浏览器中运行,那么它可能更适合使用不在浏览器中运行代码的测试框架,而是在节点中运行它以便您拥有所有可用的节点模块。如果您的目标是浏览器,我会将其重写为更专门针对浏览器。

答案 2 :(得分:0)

所以在dan和pieceOpiland的各种答案和评论的帮助下,我终于找到了做我想做的事情。

首先,pegjs和许多javascript库一样有两种格式:npm模块和bower模块。

Npm模块用于为节点创建的脚本,并从命令行调用。 Bower模块用于在浏览器中加载的脚本。

我的第一个误解就是“需要”。会在节点和浏览器中模糊地工作。这是错的。似乎要求模块以便它在浏览器中工作的唯一方法是通过异步调用,如:

require(['module'], function(module) {
  ...
});

另一个误解是我可以在浏览器中加载npm模块。某些魔法可以用于加载我的页面的各种npm文件。这可能是可能的,但只能使用一些特殊的工具,如 browserify 。如果没有特殊转换,只能在浏览器中加载bower版本。此外,pegjs bower模块的制作方式使得全局变量的定义如下:

var PEG = {
 ...
}

module.exports = PEG;

基本上,bower模块将一个全局变量(实际上是几个全局变量)插入顶级范围。

所以我没有让我的客户端代码(在浏览器中运行的那个AND节点)加载模块,而是实际加载模块:

  1. main.js通过同步要求到npm模块,如:var PEG = require('pegjs');
  2. main-browser.js通过加载bower pegjs脚本(通过<script>标签)时可用的全局变量
  3. 这些主电源&#39;然后将PEG变量注入我的解析器函数。

    为了让业力发挥作用,我只需要在生成的页面中包含pegjs bower模块( karma.conf.js 提取):

    files: [
     { pattern: 'bower_components/pegjs/peg-0.9.0.js', included: true },
     { pattern: './test/**/*.spec.js', included: false },
     { pattern: './js/**/*.js', included: false},
     './test/test-main.js',
    ],