"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; exports.__esModule = true; var path_1 = __importDefault(require("path")); var events_1 = __importDefault(require("events")); var lodash_1 = __importDefault(require("lodash")); var chokidar_1 = __importDefault(require("chokidar")); var minimatch_1 = __importDefault(require("minimatch")); var webpack_1 = __importDefault(require("webpack")); var createCompiler_1 = __importDefault(require("../webpack/compiler/createCompiler")); var createWatchCompiler_1 = __importDefault(require("../webpack/compiler/createWatchCompiler")); var registerInMemoryCompiler_1 = __importDefault(require("../webpack/compiler/registerInMemoryCompiler")); var registerReadyCallback_1 = __importDefault(require("../webpack/compiler/registerReadyCallback")); var getBuildStats_1 = __importDefault(require("../webpack/util/getBuildStats")); var webpack4GetBuildStats_1 = __importDefault(require("../webpack/util/webpack4GetBuildStats")); var createWebpackConfig_1 = __importDefault(require("./runnerUtils/createWebpackConfig")); var constants_1 = require("../util/constants"); var initMocha_1 = __importDefault(require("./runnerUtils/initMocha")); var entryPath = path_1["default"].resolve(__dirname, '../entry.js'); var entryLoaderPath = path_1["default"].resolve(__dirname, '../webpack/loader/entryLoader.js'); var includeLoaderPath = path_1["default"].resolve(__dirname, '../webpack/loader/includeFilesLoader.js'); var noop = function () { return undefined; }; var TestRunner = /** @class */ (function (_super) { __extends(TestRunner, _super); function TestRunner(entries, includes, options, cwd) { var _this = _super.call(this) || this; _this.createWebpackConfig = function () { return createWebpackConfig_1["default"]({ cwd: _this.cwd, entries: _this.entries, entryLoaderPath: entryLoaderPath, entryPath: entryPath, includeLoaderPath: includeLoaderPath, includes: _this.includes, interactive: _this.options.mochapack.interactive, webpackConfig: _this.options.webpack.config }); }; _this.entries = entries; _this.includes = includes; _this.options = options; _this.cwd = cwd; return _this; } TestRunner.prototype.prepareMocha = function (webpackConfig, stats) { var mocha = initMocha_1["default"](this.options.mocha, this.cwd); var outputPath = webpackConfig.output.path; var buildStats; if (webpack_1["default"].version[0] === '4') { buildStats = webpack4GetBuildStats_1["default"](stats, outputPath); } else { buildStats = getBuildStats_1["default"](stats, outputPath); } // @ts-ignore global.__webpackManifest__ = buildStats.affectedModules; // eslint-disable-line // clear up require cache for changed files to make sure that we get the latest changes buildStats.affectedFiles.forEach(function (filePath) { delete require.cache[filePath]; }); // Pass webpack's entry files to mocha. // Make sure to add them via `addFile` otherwise they blow away any other files // that might have been added via `--file` buildStats.entries.forEach(function (entry) { mocha.addFile(entry); }); return mocha; }; TestRunner.prototype.run = function () { return __awaiter(this, void 0, void 0, function () { var config, failures, compiler, dispose; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.createWebpackConfig()]; case 1: config = (_a.sent()).webpackConfig; failures = 0; compiler = createCompiler_1["default"](config); compiler.hooks.run.tapAsync(constants_1.MOCHAPACK_NAME, function (c, cb) { _this.emit(constants_1.WEBPACK_START_EVENT); cb(); }); dispose = registerInMemoryCompiler_1["default"](compiler); _a.label = 2; case 2: _a.trys.push([2, , 4, 5]); return [4 /*yield*/, new Promise(function (resolve, reject) { registerReadyCallback_1["default"](compiler, function (err, webpackStats) { _this.emit(constants_1.WEBPACK_READY_EVENT, err, webpackStats); if (err || !webpackStats) { reject(); return; } try { var mocha_1 = _this.prepareMocha(config, webpackStats); _this.emit(constants_1.MOCHA_BEGIN_EVENT); try { mocha_1.run(function (fails) { _this.emit(constants_1.MOCHA_FINISHED_EVENT, fails); resolve(fails); }); } catch (e) { _this.emit(constants_1.EXCEPTION_EVENT, e); resolve(1); } } catch (e) { reject(e); } }); compiler.run(noop); })]; case 3: failures = _a.sent(); return [3 /*break*/, 5]; case 4: // clean up single run dispose(); return [7 /*endfinally*/]; case 5: return [2 /*return*/, failures]; } }); }); }; TestRunner.prototype.watch = function () { return __awaiter(this, void 0, void 0, function () { var _a, config, entryConfig, mochaRunner, stats, compilationScheduler, uncaughtExceptionListener, runMocha, compiler, watchCompiler, watchOptions, pollingInterval, watcher, restartWebpackBuild, fileDeletedOrAdded; var _this = this; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, this.createWebpackConfig()]; case 1: _a = _b.sent(), config = _a.webpackConfig, entryConfig = _a.entryConfig; mochaRunner = null; stats = null; compilationScheduler = null; uncaughtExceptionListener = function (err) { // mocha catches uncaughtException only while tests are running, // that's why we register a custom error handler to keep this process alive _this.emit(constants_1.UNCAUGHT_EXCEPTION_EVENT, err); }; runMocha = function () { try { var mocha_2 = _this.prepareMocha(config, stats); // unregister our custom exception handler (see declaration) process.removeListener(constants_1.UNCAUGHT_EXCEPTION_EVENT, uncaughtExceptionListener); // run tests _this.emit(constants_1.MOCHA_BEGIN_EVENT); mochaRunner = mocha_2.run(lodash_1["default"].once(function (failures) { // register custom exception handler to catch all errors that may happen after mocha think tests are done process.on(constants_1.UNCAUGHT_EXCEPTION_EVENT, uncaughtExceptionListener); // need to wait until next tick, otherwise mochaRunner = null doesn't work.. process.nextTick(function () { mochaRunner = null; if (compilationScheduler != null) { _this.emit(constants_1.MOCHA_ABORTED_EVENT); compilationScheduler(); compilationScheduler = null; } else { _this.emit(constants_1.MOCHA_FINISHED_EVENT, failures); } }); })); } catch (err) { _this.emit(constants_1.EXCEPTION_EVENT, err); } }; compiler = createCompiler_1["default"](config); registerInMemoryCompiler_1["default"](compiler); // register webpack start callback compiler.hooks.watchRun.tapAsync(constants_1.MOCHAPACK_NAME, function (c, cb) { // check if mocha tests are still running, abort them and start compiling if (mochaRunner) { compilationScheduler = function () { _this.emit(constants_1.WEBPACK_START_EVENT); cb(); }; mochaRunner.abort(); // make sure that the current running test will be aborted when timeouts are disabled for async tests if (mochaRunner.currentRunnable) { var runnable = mochaRunner.currentRunnable; runnable.retries(0); runnable.timeout(1); runnable.resetTimeout(1); } } else { _this.emit(constants_1.WEBPACK_START_EVENT); cb(); } }); // register webpack ready callback registerReadyCallback_1["default"](compiler, function (err, webpackStats) { _this.emit(constants_1.WEBPACK_READY_EVENT, err, webpackStats); if (err) { // wait for fixed tests return; } stats = webpackStats; runMocha(); }); watchCompiler = createWatchCompiler_1["default"](compiler, config.watchOptions); // start webpack build immediately watchCompiler.watch(); watchOptions = watchCompiler.getWatchOptions(); pollingInterval = typeof watchOptions.poll === 'number' ? watchOptions.poll : undefined; watcher = chokidar_1["default"].watch(this.entries, { cwd: this.cwd, // see https://github.com/webpack/watchpack/blob/e5305b53ac3cf2a70d49a772912b115fa77665c2/lib/DirectoryWatcher.js ignoreInitial: true, persistent: true, followSymlinks: false, ignorePermissionErrors: true, ignored: watchOptions.ignored, usePolling: watchOptions.poll ? true : undefined, interval: pollingInterval, binaryInterval: pollingInterval }); restartWebpackBuild = lodash_1["default"].debounce(function () { return watchCompiler.watch(); }, watchOptions.aggregateTimeout); fileDeletedOrAdded = function (file, deleted) { var matchesGlob = _this.entries.some(function (pattern) { return minimatch_1["default"](file, pattern); }); // Chokidar gives files not matching pattern sometimes, prevent this if (matchesGlob) { var filePath = path_1["default"].join(_this.cwd, file); if (deleted) { _this.emit(constants_1.ENTRY_REMOVED_EVENT, file); entryConfig.removeFile(filePath); } else { _this.emit(constants_1.ENTRY_ADDED_EVENT, file); entryConfig.addFile(filePath); } // pause webpack watch immediately before webpack will be notified watchCompiler.pause(); // call debounced webpack runner to rebuild files restartWebpackBuild(); } }; // add listener for entry creation & deletion events watcher.on('add', function (file) { return fileDeletedOrAdded(file, false); }); watcher.on('unlink', function (file) { return fileDeletedOrAdded(file, true); }); return [2 /*return*/, new Promise(function () { return undefined; })]; // never ending story } }); }); }; return TestRunner; }(events_1["default"])); exports["default"] = TestRunner; //# sourceMappingURL=TestRunner.js.map