“写完以后”:如何用watchify模仿大口吃?

时间:2018-08-25 22:16:28

标签: node.js gulp gulp-watch node-streams watchify

以下Gulp任务几乎完成了我想要的。

const gulp = require('gulp');
const browserify = require('browserify');
const vinylStream = require('vinyl-source-stream');
const vinylBuffer = require('vinyl-buffer');
const watchify = require('watchify');
const glob = require('glob');
const jasmineBrowser = require('gulp-jasmine-browser');

gulp.task('test', function() {
    let testBundler = browserify({
        entries: glob.sync('src/**/*-test.js'),
        cache: {},
        packageCache: {},
    }).plugin(watchify);
    function updateSpecs() {
        return testBundler.bundle()
            .pipe(vinylStream(jsBundleName))
            .pipe(vinylBuffer())
            .pipe(jasmineBrowser.specRunner({console: true}))
            .pipe(jasmineBrowser.headless({driver: 'phantomjs'}));
    }
    testBundler.on('update', updateSpecs);
    updateSpecs();
});

它使用Browserify捆绑了我所有的Jasmine规格,并通过gulp-jasmine-browser进行了测试。它还会监视它们依赖的所有规格和所有模块,并在这些模块中的任何一个发生更改时重新运行测试。

我真正想解决的唯一丑陋之处是,每次运行updateSpecs时都会创建一个新的PhantomJS实例和一个新的Jasmine服务器。我希望通过以下代码避免这种情况:

gulp.task('test', function() {
    let testBundler = browserify({
        entries: glob.sync('src/**/*-test.js'),
        cache: {},
        packageCache: {},
    }).plugin(watchify);
    // persist the Jasmine server and PhantomJS browser
    let testServer = jasmineBrowser.headless({driver: 'phantomjs'});
    function updateSpecs() {
        return testBundler.bundle()
            .pipe(vinylStream(jsBundleName))
            .pipe(vinylBuffer())
            .pipe(jasmineBrowser.specRunner({console: true}))
            .pipe(testServer);
    }
    testBundler.on('update', updateSpecs);
    updateSpecs();
});

A,这行不通。在开始任务之后,所有测试都可以正常运行,但是下次调用updateSpecs时,我收到一个write after end错误,并且该任务以状态1退出。此错误源自readable-stream节点模块。

据我所知,end的第一次运行期间的updateSpecs事件使testServer处于不接受任何新输入的状态。不幸的是,Node.js streams documentation对于如何解决这个问题尚不清楚。

我曾尝试在不同的位置断开管道链,但是得到的结果相同,这似乎表明这是流的普遍行为。我还尝试通过插入不重新发送该事件的流来阻止end事件的传播,但这完全无法运行测试。最后,我尝试return从任务中testServer流;这停止了​​错误,但是尽管每次更改源时都会调用updateSpecs函数,但是测试仅在任务第一次启动时运行。这次,testServer似乎只是忽略了新输入。

gulp-jasmine-browser文档建议使用以下代码:

var watch = require('gulp-watch');

gulp.task('test', function() {
    var filesForTest = ['src/**/*.js', 'spec/**/*-test.js'];
    return gulp.src(filesForTest)
        .pipe(watch(filesForTest))
        .pipe(jasmineBrowser.specRunner())
        .pipe(jasmineBrowser.server());
});

并继续建议您也可以使用Browserify进行此操作,但这没有说明。显然,gulp-watch会执行某些操作,从而导致后续管道在以后接受更新的输入。我该如何通过watchify模仿这种行为?

1 个答案:

答案 0 :(得分:0)

GitHub issue

事实证明,在end事件之后,您无法编写Node.js中的硬规则。此外,jasmineBrowser.specRunner().server().headless() 必须接收end信号以便实际测试任何东西。此限制是从官方茉莉花测试运行程序继承的。

由于相同的原因,自述文件中带有gulp-watch的示例也不起作用。为了使其正常工作,必须执行与我在问题中的watchify代码的工作版本相似的事情:

gulp.task('test', function() {
    var filesForTest = ['src/**/*.js', 'spec/**/*-test.js'];
    function runTests() {
        return gulp.src(filesForTest)
            .pipe(jasmineBrowser.specRunner())
            .pipe(jasmineBrowser.server());
    }
    watch(filesForTest).on('add change unlink', runTests);
});

(我没有对其进行测试,但是应该可以进行一些非常接近的测试。)

因此,无论您使用何种监视机制,始终需要在每个周期再次调用.specRunner().server()。好消息是,显然,如果您明确传递端口号,Jasmine服务器将被重用:

            .pipe(jasmineBrowser.server({port: 8080}));

这也适用于.headless()