12

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

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的需要引导文件,它是这样配置的:

'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([])

因此,我尝试以 karma.conf.js 的方式将pegjs包含在 Karma 加载的文件中:

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 文件夹中,但没有成功。

所以我不知道要从这里去......在谷歌或这里找不到任何相关的东西。使用 karma 来 requirejs/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:终于理解并找到了一个可以接受的解决方案。

4

3 回答 3

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'
],

Included 的值指示 karma 服务器生成的网页是否应具有该文件的脚本标记(请参阅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'
],

将导致 karma 生成一个带有类似 head 标签的网页:

<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.

我相信这标志着它是一项预处理工作,但我不完全确定为什么会产生如此大的差异。

于 2015-11-29T22:00:31.717 回答
1

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

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

浏览器不具备同步执行此操作的功能:var PEG = require('pegjs'). 这就是为什么它要求您使用require([])在 pegjs 完成加载时传递要执行的回调的原因。

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

它也无法从文件系统加载文件,fs.readFileSync(grammarFile, 'utf8')因此您必须以另一种方式进行。你可以让 Karma 托管你的 peg 语法,方法是把它放在 files 数组中,然后使用requirejs 文本插件加载它。

如果您正在测试的模块旨在在 node.js 上而不是在浏览器中运行,那么它可能更适合使用不在浏览器中运行代码但在节点中运行它的测试框架,因此您拥有所有可供您使用的节点模块。如果您的目标是浏览器,我会重写它以更具体地针对浏览器。

于 2015-12-01T10:17:12.373 回答
0

因此,在 dan 和 pieceOpiland 的各种答案和评论的帮助下,我终于找到了一种方法来做我想做的事。

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

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

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

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

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

var PEG = {
 ...
}

module.exports = PEG;

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

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

  1. main.js 通过对 npm 模块的同步要求,如下所示:var PEG = require('pegjs');
  2. main-browser.js 通过全局变量,当您加载 bower pegjs 脚本(通过<script>标签)时该变量可用

然后,这两个“主电源”都将 PEG 变量注入我的解析器函数。

为了使 karma 工作,我只需要在生成的页面中包含 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',
],
于 2015-12-02T07:32:20.667 回答