UNPKG

@typed/test

Version:
182 lines 9.83 kB
"use strict"; 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 __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const fs_1 = require("fs"); const http_1 = require("http"); const lodash_isequal_1 = __importDefault(require("lodash.isequal")); const path_1 = require("path"); const resolve_1 = require("resolve"); const tempy_1 = __importDefault(require("tempy")); const createIndexHtml_1 = require("../browser/createIndexHtml"); const findOpenPort_1 = require("../browser/findOpenPort"); const generateTestBundle_1 = require("../browser/generateTestBundle"); const openBrowser_1 = require("../browser/openBrowser"); const server_1 = require("../browser/server"); const watchFile_1 = require("../browser/webpack/watchFile"); const collectByKey_1 = require("../common/collectByKey"); const makeAbsolute_1 = require("../common/makeAbsolute"); const results_1 = require("../results"); const watchTestMetadata_1 = require("../tests/watchTestMetadata"); const typeCheckInAnotherProcess_1 = require("../typescript/typeCheckInAnotherProcess"); const Results_1 = require("./Results"); const uniq = (list) => Array.from(new Set(list)); const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); const updateQueue = (metadata, queue) => { const queuedMetadataByFilePath = collectByKey_1.collectByKey(x => x.filePath, queue); const queuedFilePaths = Object.keys(queuedMetadataByFilePath).sort(); const metadataByFilePath = collectByKey_1.collectByKey(x => x.filePath, metadata); const filePaths = Object.keys(metadataByFilePath).sort(); const allFilePaths = uniq([...filePaths, ...queuedFilePaths]); return allFilePaths.reduce((xs, filePath) => metadataByFilePath[filePath] // replace updated files ? xs.concat(metadataByFilePath[filePath]) : xs.concat(queuedMetadataByFilePath[filePath]), []); }; function watchBrowserTests(fileGlobs, compilerOptions, options, cwd, logger, cb, errCb, typeCheckCb) { return __awaiter(this, void 0, void 0, function* () { const { keepAlive, timeout, typeCheck, mode } = options; const outputDirectory = tempy_1.default.directory(); const temporaryPath = path_1.join(outputDirectory, path_1.basename(tempy_1.default.file({ extension: 'ts' }))); const bundlePath = path_1.join(outputDirectory, path_1.basename(tempy_1.default.file({ extension: 'js' }))); const indexHtmlPath = path_1.join(outputDirectory, 'index.html'); const port = yield findOpenPort_1.findOpenPort(); const url = `http://localhost:${port}`; const logErrors = (errors) => Promise.all(errors.map(logger.error)); const makeAbsolutePath = (path) => (path_1.isAbsolute(path) ? path : path_1.join(cwd, path)); const { updateResults, removeFilePath } = new Results_1.Results(); // shared mutable state let queuedMetadata = []; let previousMetadata = []; let testsAreRunning = false; let writingToDisk = false; let scheduleNextRunHandle; let newFileOnDisk = false; let killBrowser = () => void 0; let previousWebpackHash = ''; let firstRun = true; let timeToRunTests = 0; let browserOpenTime = 0; let testsCompletedTime = 0; const killTestBrowser = () => (!keepAlive && killBrowser ? killBrowser() : void 0); const setupTestBrowser = () => __awaiter(this, void 0, void 0, function* () { const { browser, keepAlive } = options; if (browser !== 'chrome-headless') { const launcher = yield openBrowser_1.getLauncher(); const run = () => __awaiter(this, void 0, void 0, function* () { killTestBrowser(); logger.log('Opening browser...'); browserOpenTime = Date.now(); const instance = yield openBrowser_1.openBrowser(browser, url, keepAlive, launcher); if (!firstRun) { const lastCompletion = testsCompletedTime; // Re-run tests if they seem to have stalled - this can happen when browsers are opened in succession quite quickly // I've only been able to make it do this when I am very rapidly changing and saving a test file to purposely try and break things. delay(timeToRunTests * 3).then(() => (testsCompletedTime === lastCompletion ? run() : void 0)); } killBrowser = () => instance.stop(); }); return run; } const { launch } = require(resolve_1.sync('chrome-launcher', { basedir: cwd })); const run = () => __awaiter(this, void 0, void 0, function* () { killTestBrowser(); logger.log('Opening browser...'); browserOpenTime = Date.now(); const chrome = yield launch({ startingUrl: url }); if (!firstRun) { const lastCompletion = testsCompletedTime; delay(timeToRunTests * 3).then(() => (testsCompletedTime === lastCompletion ? run() : void 0)); } killBrowser = () => chrome.kill(); }); return run; }); const runTestsInBrowser = yield setupTestBrowser(); const newStats = (stats) => __awaiter(this, void 0, void 0, function* () { const shouldReturnEarly = stats.hash === previousWebpackHash || !newFileOnDisk || testsAreRunning; if (shouldReturnEarly) { previousWebpackHash = stats.hash; return; } if (stats.hasErrors()) { const { errors } = stats.toJson(); return logErrors(errors); } testsAreRunning = true; runTestsInBrowser(); }); const writeToDisk = (metadata) => { fs_1.writeFileSync(temporaryPath, generateTestBundle_1.generateTestBundle(cwd, outputDirectory, port, timeout, metadata)); newFileOnDisk = true; writingToDisk = false; if (firstRun) { watchFile_1.watchFile(cwd, temporaryPath, bundlePath, options.webpackConfiguration, newStats, errCb); } }; const typeCheckMetadata = (metadata) => __awaiter(this, void 0, void 0, function* () { logger.log('Typechecking...'); const processResults = yield typeCheckInAnotherProcess_1.typecheckInAnotherProcess(cwd, metadata.map(x => x.filePath)); logger.log('Type checking complete'); typeCheckCb(processResults); }); const removeFileFromQueue = (filePath) => { const path = makeAbsolute_1.makeAbsolute(cwd, filePath); removeFilePath(path); queuedMetadata = queuedMetadata.filter(x => makeAbsolutePath(x.filePath) !== path); }; const scheduleNextBundleWrite = () => { if (testsAreRunning || writingToDisk) { clearTimeout(scheduleNextRunHandle); scheduleNextRunHandle = setTimeout(scheduleNextBundleWrite, 600); return; } writingToDisk = true; const currentQueue = queuedMetadata.slice(); writeToDisk(currentQueue); if (typeCheck) { typeCheckMetadata(currentQueue); } queuedMetadata = []; }; const updateMetadataAndWriteBundle = (metadata) => __awaiter(this, void 0, void 0, function* () { queuedMetadata = updateQueue(metadata, queuedMetadata); if (lodash_isequal_1.default(metadata, previousMetadata) || queuedMetadata.length === 0) { return; } previousMetadata = metadata; scheduleNextBundleWrite(); }); const server = http_1.createServer(server_1.setupServer(logger, outputDirectory, newResults => { firstRun = false; if (!keepAlive && killBrowser) { killBrowser(); } const results = updateResults(newResults); const stats = results_1.getTestStats(results_1.getTestResults(results)); cb({ results, stats }); testsCompletedTime = Date.now(); timeToRunTests = testsCompletedTime - browserOpenTime; newFileOnDisk = testsAreRunning = false; })); fs_1.writeFileSync(indexHtmlPath, createIndexHtml_1.createIndexHtml(path_1.basename(bundlePath))); server.listen(port, '0.0.0.0'); const { dispose: stopWatchingMetadata } = yield watchTestMetadata_1.watchTestMetadata(cwd, fileGlobs, compilerOptions, mode, logger, removeFileFromQueue, updateMetadataAndWriteBundle); const dispose = () => { stopWatchingMetadata(); server.close(); }; return { dispose }; }); } exports.watchBrowserTests = watchBrowserTests; //# sourceMappingURL=watchBrowserTests.js.map