Следующая задача 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();
});
Он объединяет все мои спецификации Jasmine с помощью Browserify и тестирует их с помощью глоток-жасмин-браузер. Он также отслеживает все спецификации и все модули, от которых они зависят, и повторно запускает тесты, если какой-либо из этих модулей изменяется.
Единственный неприятный момент, который я бы очень хотел решить, - это то, что новый экземпляр PhantomJS и новый сервер Jasmine создаются каждый раз при запуске updateSpecs
. Я надеялся избежать этого с помощью следующего кода:
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();
});
Увы, это не работает. Сразу после запуска задачи все тесты выполняются нормально, но при следующем вызове updateSpecs
я получаю ошибку write after end
, и задача завершается со статусом 1. Эта ошибка возникает из модуля узла readable-stream
.
Насколько я понимаю, событие end
во время первого запуска updateSpecs
оставляет testServer
в состоянии, в котором он не принимает никаких новых входных данных. К сожалению, в Документация по потокам Node.js не очень понятно, как это исправить.
Я попытался разорвать цепочку каналов в другом месте, но получил тот же результат, который, кажется, указывает на то, что это универсальное поведение для потоков. Я также попытался остановить распространение события 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?
Как оказалось, в Node.js есть жесткое правило, что нельзя писать после события end
. Кроме того, jasmineBrowser.specRunner()
, .server()
и .headless()
должен принимают сигнал end
, чтобы фактически что-либо протестировать. Это ограничение унаследовано от официального средства запуска тестов Jasmine.
Пример с gulp-watch
из README тоже не работает по той же причине. Чтобы заставить его работать, нужно было бы сделать что-то похожее на рабочую версию моего кода 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()
.