watch.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.default = watch;
  6. function _path() {
  7. const data = _interopRequireDefault(require('path'));
  8. _path = function _path() {
  9. return data;
  10. };
  11. return data;
  12. }
  13. function _ansiEscapes() {
  14. const data = _interopRequireDefault(require('ansi-escapes'));
  15. _ansiEscapes = function _ansiEscapes() {
  16. return data;
  17. };
  18. return data;
  19. }
  20. function _chalk() {
  21. const data = _interopRequireDefault(require('chalk'));
  22. _chalk = function _chalk() {
  23. return data;
  24. };
  25. return data;
  26. }
  27. function _exit() {
  28. const data = _interopRequireDefault(require('exit'));
  29. _exit = function _exit() {
  30. return data;
  31. };
  32. return data;
  33. }
  34. function _slash() {
  35. const data = _interopRequireDefault(require('slash'));
  36. _slash = function _slash() {
  37. return data;
  38. };
  39. return data;
  40. }
  41. function _jestMessageUtil() {
  42. const data = require('jest-message-util');
  43. _jestMessageUtil = function _jestMessageUtil() {
  44. return data;
  45. };
  46. return data;
  47. }
  48. function _jestUtil() {
  49. const data = require('jest-util');
  50. _jestUtil = function _jestUtil() {
  51. return data;
  52. };
  53. return data;
  54. }
  55. function _jestValidate() {
  56. const data = require('jest-validate');
  57. _jestValidate = function _jestValidate() {
  58. return data;
  59. };
  60. return data;
  61. }
  62. function _jestResolve() {
  63. const data = _interopRequireDefault(require('jest-resolve'));
  64. _jestResolve = function _jestResolve() {
  65. return data;
  66. };
  67. return data;
  68. }
  69. function _jestWatcher() {
  70. const data = require('jest-watcher');
  71. _jestWatcher = function _jestWatcher() {
  72. return data;
  73. };
  74. return data;
  75. }
  76. var _getChangedFilesPromise = _interopRequireDefault(
  77. require('./getChangedFilesPromise')
  78. );
  79. var _is_valid_path = _interopRequireDefault(require('./lib/is_valid_path'));
  80. var _create_context = _interopRequireDefault(require('./lib/create_context'));
  81. var _runJest = _interopRequireDefault(require('./runJest'));
  82. var _update_global_config = _interopRequireDefault(
  83. require('./lib/update_global_config')
  84. );
  85. var _SearchSource = _interopRequireDefault(require('./SearchSource'));
  86. var _TestWatcher = _interopRequireDefault(require('./TestWatcher'));
  87. var _FailedTestsCache = _interopRequireDefault(require('./FailedTestsCache'));
  88. var _test_path_pattern = _interopRequireDefault(
  89. require('./plugins/test_path_pattern')
  90. );
  91. var _test_name_pattern = _interopRequireDefault(
  92. require('./plugins/test_name_pattern')
  93. );
  94. var _update_snapshots = _interopRequireDefault(
  95. require('./plugins/update_snapshots')
  96. );
  97. var _update_snapshots_interactive = _interopRequireDefault(
  98. require('./plugins/update_snapshots_interactive')
  99. );
  100. var _quit = _interopRequireDefault(require('./plugins/quit'));
  101. var _watch_plugins_helpers = require('./lib/watch_plugins_helpers');
  102. var _active_filters_message = _interopRequireDefault(
  103. require('./lib/active_filters_message')
  104. );
  105. function _interopRequireDefault(obj) {
  106. return obj && obj.__esModule ? obj : {default: obj};
  107. }
  108. /**
  109. * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  110. *
  111. * This source code is licensed under the MIT license found in the
  112. * LICENSE file in the root directory of this source tree.
  113. */
  114. const preRunMessagePrint = _jestUtil().preRunMessage.print;
  115. let hasExitListener = false;
  116. const INTERNAL_PLUGINS = [
  117. _test_path_pattern.default,
  118. _test_name_pattern.default,
  119. _update_snapshots.default,
  120. _update_snapshots_interactive.default,
  121. _quit.default
  122. ];
  123. const RESERVED_KEY_PLUGINS = new Map([
  124. [
  125. _update_snapshots.default,
  126. {
  127. forbiddenOverwriteMessage: 'updating snapshots',
  128. key: 'u'
  129. }
  130. ],
  131. [
  132. _update_snapshots_interactive.default,
  133. {
  134. forbiddenOverwriteMessage: 'updating snapshots interactively',
  135. key: 'i'
  136. }
  137. ],
  138. [
  139. _quit.default,
  140. {
  141. forbiddenOverwriteMessage: 'quitting watch mode'
  142. }
  143. ]
  144. ]);
  145. function watch(
  146. initialGlobalConfig,
  147. contexts,
  148. outputStream,
  149. hasteMapInstances,
  150. stdin = process.stdin,
  151. hooks = new (_jestWatcher()).JestHook(),
  152. filter
  153. ) {
  154. // `globalConfig` will be constantly updated and reassigned as a result of
  155. // watch mode interactions.
  156. let globalConfig = initialGlobalConfig;
  157. let activePlugin;
  158. globalConfig = (0, _update_global_config.default)(globalConfig, {
  159. mode: globalConfig.watch ? 'watch' : 'watchAll',
  160. passWithNoTests: true
  161. });
  162. const updateConfigAndRun = ({
  163. bail,
  164. changedSince,
  165. collectCoverage,
  166. collectCoverageFrom,
  167. collectCoverageOnlyFrom,
  168. coverageDirectory,
  169. coverageReporters,
  170. mode,
  171. notify,
  172. notifyMode,
  173. onlyFailures,
  174. reporters,
  175. testNamePattern,
  176. testPathPattern,
  177. updateSnapshot,
  178. verbose
  179. } = {}) => {
  180. const previousUpdateSnapshot = globalConfig.updateSnapshot;
  181. globalConfig = (0, _update_global_config.default)(globalConfig, {
  182. bail,
  183. changedSince,
  184. collectCoverage,
  185. collectCoverageFrom,
  186. collectCoverageOnlyFrom,
  187. coverageDirectory,
  188. coverageReporters,
  189. mode,
  190. notify,
  191. notifyMode,
  192. onlyFailures,
  193. reporters,
  194. testNamePattern,
  195. testPathPattern,
  196. updateSnapshot,
  197. verbose
  198. });
  199. startRun(globalConfig);
  200. globalConfig = (0, _update_global_config.default)(globalConfig, {
  201. // updateSnapshot is not sticky after a run.
  202. updateSnapshot:
  203. previousUpdateSnapshot === 'all' ? 'none' : previousUpdateSnapshot
  204. });
  205. };
  206. const watchPlugins = INTERNAL_PLUGINS.map(
  207. InternalPlugin =>
  208. new InternalPlugin({
  209. stdin,
  210. stdout: outputStream
  211. })
  212. );
  213. watchPlugins.forEach(plugin => {
  214. const hookSubscriber = hooks.getSubscriber();
  215. if (plugin.apply) {
  216. plugin.apply(hookSubscriber);
  217. }
  218. });
  219. if (globalConfig.watchPlugins != null) {
  220. const watchPluginKeys = new Map();
  221. var _iteratorNormalCompletion = true;
  222. var _didIteratorError = false;
  223. var _iteratorError = undefined;
  224. try {
  225. for (
  226. var _iterator = watchPlugins[Symbol.iterator](), _step;
  227. !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
  228. _iteratorNormalCompletion = true
  229. ) {
  230. const plugin = _step.value;
  231. const reservedInfo = RESERVED_KEY_PLUGINS.get(plugin.constructor) || {};
  232. const key = reservedInfo.key || getPluginKey(plugin, globalConfig);
  233. if (!key) {
  234. continue;
  235. }
  236. const forbiddenOverwriteMessage =
  237. reservedInfo.forbiddenOverwriteMessage;
  238. watchPluginKeys.set(key, {
  239. forbiddenOverwriteMessage,
  240. overwritable: forbiddenOverwriteMessage == null,
  241. plugin
  242. });
  243. }
  244. } catch (err) {
  245. _didIteratorError = true;
  246. _iteratorError = err;
  247. } finally {
  248. try {
  249. if (!_iteratorNormalCompletion && _iterator.return != null) {
  250. _iterator.return();
  251. }
  252. } finally {
  253. if (_didIteratorError) {
  254. throw _iteratorError;
  255. }
  256. }
  257. }
  258. var _iteratorNormalCompletion2 = true;
  259. var _didIteratorError2 = false;
  260. var _iteratorError2 = undefined;
  261. try {
  262. for (
  263. var _iterator2 = globalConfig.watchPlugins[Symbol.iterator](), _step2;
  264. !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done);
  265. _iteratorNormalCompletion2 = true
  266. ) {
  267. const pluginWithConfig = _step2.value;
  268. let plugin;
  269. try {
  270. const ThirdPartyPlugin = require(pluginWithConfig.path);
  271. plugin = new ThirdPartyPlugin({
  272. config: pluginWithConfig.config,
  273. stdin,
  274. stdout: outputStream
  275. });
  276. } catch (error) {
  277. const errorWithContext = new Error(
  278. `Failed to initialize watch plugin "${_chalk().default.bold(
  279. (0, _slash().default)(
  280. _path().default.relative(process.cwd(), pluginWithConfig.path)
  281. )
  282. )}":\n\n${(0, _jestMessageUtil().formatExecError)(
  283. error,
  284. contexts[0].config,
  285. {
  286. noStackTrace: false
  287. }
  288. )}`
  289. );
  290. delete errorWithContext.stack;
  291. return Promise.reject(errorWithContext);
  292. }
  293. checkForConflicts(watchPluginKeys, plugin, globalConfig);
  294. const hookSubscriber = hooks.getSubscriber();
  295. if (plugin.apply) {
  296. plugin.apply(hookSubscriber);
  297. }
  298. watchPlugins.push(plugin);
  299. }
  300. } catch (err) {
  301. _didIteratorError2 = true;
  302. _iteratorError2 = err;
  303. } finally {
  304. try {
  305. if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
  306. _iterator2.return();
  307. }
  308. } finally {
  309. if (_didIteratorError2) {
  310. throw _iteratorError2;
  311. }
  312. }
  313. }
  314. }
  315. const failedTestsCache = new _FailedTestsCache.default();
  316. let searchSources = contexts.map(context => ({
  317. context,
  318. searchSource: new _SearchSource.default(context)
  319. }));
  320. let isRunning = false;
  321. let testWatcher;
  322. let shouldDisplayWatchUsage = true;
  323. let isWatchUsageDisplayed = false;
  324. const emitFileChange = () => {
  325. if (hooks.isUsed('onFileChange')) {
  326. const projects = searchSources.map(({context, searchSource}) => ({
  327. config: context.config,
  328. testPaths: searchSource.findMatchingTests('').tests.map(t => t.path)
  329. }));
  330. hooks.getEmitter().onFileChange({
  331. projects
  332. });
  333. }
  334. };
  335. emitFileChange();
  336. hasteMapInstances.forEach((hasteMapInstance, index) => {
  337. hasteMapInstance.on('change', ({eventsQueue, hasteFS, moduleMap}) => {
  338. const validPaths = eventsQueue.filter(({filePath}) =>
  339. (0, _is_valid_path.default)(globalConfig, filePath)
  340. );
  341. if (validPaths.length) {
  342. const context = (contexts[index] = (0, _create_context.default)(
  343. contexts[index].config,
  344. {
  345. hasteFS,
  346. moduleMap
  347. }
  348. ));
  349. activePlugin = null;
  350. searchSources = searchSources.slice();
  351. searchSources[index] = {
  352. context,
  353. searchSource: new _SearchSource.default(context)
  354. };
  355. emitFileChange();
  356. startRun(globalConfig);
  357. }
  358. });
  359. });
  360. if (!hasExitListener) {
  361. hasExitListener = true;
  362. process.on('exit', () => {
  363. if (activePlugin) {
  364. outputStream.write(_ansiEscapes().default.cursorDown());
  365. outputStream.write(_ansiEscapes().default.eraseDown);
  366. }
  367. });
  368. }
  369. const startRun = globalConfig => {
  370. if (isRunning) {
  371. return Promise.resolve(null);
  372. }
  373. testWatcher = new _TestWatcher.default({
  374. isWatchMode: true
  375. });
  376. _jestUtil().isInteractive &&
  377. outputStream.write(_jestUtil().specialChars.CLEAR);
  378. preRunMessagePrint(outputStream);
  379. isRunning = true;
  380. const configs = contexts.map(context => context.config);
  381. const changedFilesPromise = (0, _getChangedFilesPromise.default)(
  382. globalConfig,
  383. configs
  384. ); // Clear cache for required modules
  385. _jestResolve().default.clearDefaultResolverCache();
  386. return (0, _runJest.default)({
  387. changedFilesPromise,
  388. contexts,
  389. failedTestsCache,
  390. filter,
  391. globalConfig,
  392. jestHooks: hooks.getEmitter(),
  393. onComplete: results => {
  394. isRunning = false;
  395. hooks.getEmitter().onTestRunComplete(results); // Create a new testWatcher instance so that re-runs won't be blocked.
  396. // The old instance that was passed to Jest will still be interrupted
  397. // and prevent test runs from the previous run.
  398. testWatcher = new _TestWatcher.default({
  399. isWatchMode: true
  400. }); // Do not show any Watch Usage related stuff when running in a
  401. // non-interactive environment
  402. if (_jestUtil().isInteractive) {
  403. if (shouldDisplayWatchUsage) {
  404. outputStream.write(usage(globalConfig, watchPlugins));
  405. shouldDisplayWatchUsage = false; // hide Watch Usage after first run
  406. isWatchUsageDisplayed = true;
  407. } else {
  408. outputStream.write(showToggleUsagePrompt());
  409. shouldDisplayWatchUsage = false;
  410. isWatchUsageDisplayed = false;
  411. }
  412. } else {
  413. outputStream.write('\n');
  414. }
  415. failedTestsCache.setTestResults(results.testResults);
  416. },
  417. outputStream,
  418. startRun,
  419. testWatcher
  420. }).catch((
  421. error // Errors thrown inside `runJest`, e.g. by resolvers, are caught here for
  422. ) =>
  423. // continuous watch mode execution. We need to reprint them to the
  424. // terminal and give just a little bit of extra space so they fit below
  425. // `preRunMessagePrint` message nicely.
  426. console.error(
  427. '\n\n' +
  428. (0, _jestMessageUtil().formatExecError)(error, contexts[0].config, {
  429. noStackTrace: false
  430. })
  431. )
  432. );
  433. };
  434. const onKeypress = key => {
  435. if (
  436. key === _jestWatcher().KEYS.CONTROL_C ||
  437. key === _jestWatcher().KEYS.CONTROL_D
  438. ) {
  439. if (typeof stdin.setRawMode === 'function') {
  440. stdin.setRawMode(false);
  441. }
  442. outputStream.write('\n');
  443. (0, _exit().default)(0);
  444. return;
  445. }
  446. if (activePlugin != null && activePlugin.onKey) {
  447. // if a plugin is activate, Jest should let it handle keystrokes, so ignore
  448. // them here
  449. activePlugin.onKey(key);
  450. return;
  451. } // Abort test run
  452. const pluginKeys = (0, _watch_plugins_helpers.getSortedUsageRows)(
  453. watchPlugins,
  454. globalConfig
  455. ).map(usage => Number(usage.key).toString(16));
  456. if (
  457. isRunning &&
  458. testWatcher &&
  459. ['q', _jestWatcher().KEYS.ENTER, 'a', 'o', 'f']
  460. .concat(pluginKeys)
  461. .includes(key)
  462. ) {
  463. testWatcher.setState({
  464. interrupted: true
  465. });
  466. return;
  467. }
  468. const matchingWatchPlugin = (0,
  469. _watch_plugins_helpers.filterInteractivePlugins)(
  470. watchPlugins,
  471. globalConfig
  472. ).find(plugin => getPluginKey(plugin, globalConfig) === key);
  473. if (matchingWatchPlugin != null) {
  474. if (isRunning) {
  475. testWatcher.setState({
  476. interrupted: true
  477. });
  478. return;
  479. } // "activate" the plugin, which has jest ignore keystrokes so the plugin
  480. // can handle them
  481. activePlugin = matchingWatchPlugin;
  482. if (activePlugin.run) {
  483. activePlugin.run(globalConfig, updateConfigAndRun).then(
  484. shouldRerun => {
  485. activePlugin = null;
  486. if (shouldRerun) {
  487. updateConfigAndRun();
  488. }
  489. },
  490. () => {
  491. activePlugin = null;
  492. onCancelPatternPrompt();
  493. }
  494. );
  495. } else {
  496. activePlugin = null;
  497. }
  498. }
  499. switch (key) {
  500. case _jestWatcher().KEYS.ENTER:
  501. startRun(globalConfig);
  502. break;
  503. case 'a':
  504. globalConfig = (0, _update_global_config.default)(globalConfig, {
  505. mode: 'watchAll',
  506. testNamePattern: '',
  507. testPathPattern: ''
  508. });
  509. startRun(globalConfig);
  510. break;
  511. case 'c':
  512. updateConfigAndRun({
  513. mode: 'watch',
  514. testNamePattern: '',
  515. testPathPattern: ''
  516. });
  517. break;
  518. case 'f':
  519. globalConfig = (0, _update_global_config.default)(globalConfig, {
  520. onlyFailures: !globalConfig.onlyFailures
  521. });
  522. startRun(globalConfig);
  523. break;
  524. case 'o':
  525. globalConfig = (0, _update_global_config.default)(globalConfig, {
  526. mode: 'watch',
  527. testNamePattern: '',
  528. testPathPattern: ''
  529. });
  530. startRun(globalConfig);
  531. break;
  532. case '?':
  533. break;
  534. case 'w':
  535. if (!shouldDisplayWatchUsage && !isWatchUsageDisplayed) {
  536. outputStream.write(_ansiEscapes().default.cursorUp());
  537. outputStream.write(_ansiEscapes().default.eraseDown);
  538. outputStream.write(usage(globalConfig, watchPlugins));
  539. isWatchUsageDisplayed = true;
  540. shouldDisplayWatchUsage = false;
  541. }
  542. break;
  543. }
  544. };
  545. const onCancelPatternPrompt = () => {
  546. outputStream.write(_ansiEscapes().default.cursorHide);
  547. outputStream.write(_jestUtil().specialChars.CLEAR);
  548. outputStream.write(usage(globalConfig, watchPlugins));
  549. outputStream.write(_ansiEscapes().default.cursorShow);
  550. };
  551. if (typeof stdin.setRawMode === 'function') {
  552. stdin.setRawMode(true);
  553. stdin.resume();
  554. stdin.setEncoding('utf8');
  555. stdin.on('data', onKeypress);
  556. }
  557. startRun(globalConfig);
  558. return Promise.resolve();
  559. }
  560. const checkForConflicts = (watchPluginKeys, plugin, globalConfig) => {
  561. const key = getPluginKey(plugin, globalConfig);
  562. if (!key) {
  563. return;
  564. }
  565. const conflictor = watchPluginKeys.get(key);
  566. if (!conflictor || conflictor.overwritable) {
  567. watchPluginKeys.set(key, {
  568. overwritable: false,
  569. plugin
  570. });
  571. return;
  572. }
  573. let error;
  574. if (conflictor.forbiddenOverwriteMessage) {
  575. error = `
  576. Watch plugin ${_chalk().default.bold.red(
  577. getPluginIdentifier(plugin)
  578. )} attempted to register key ${_chalk().default.bold.red(`<${key}>`)},
  579. that is reserved internally for ${_chalk().default.bold.red(
  580. conflictor.forbiddenOverwriteMessage
  581. )}.
  582. Please change the configuration key for this plugin.`.trim();
  583. } else {
  584. const plugins = [conflictor.plugin, plugin]
  585. .map(p => _chalk().default.bold.red(getPluginIdentifier(p)))
  586. .join(' and ');
  587. error = `
  588. Watch plugins ${plugins} both attempted to register key ${_chalk().default.bold.red(
  589. `<${key}>`
  590. )}.
  591. Please change the key configuration for one of the conflicting plugins to avoid overlap.`.trim();
  592. }
  593. throw new (_jestValidate()).ValidationError(
  594. 'Watch plugin configuration error',
  595. error
  596. );
  597. };
  598. const getPluginIdentifier = (
  599. plugin // This breaks as `displayName` is not defined as a static, but since
  600. // WatchPlugin is an interface, and it is my understanding interface
  601. // static fields are not definable anymore, no idea how to circumvent
  602. // this :-(
  603. // @ts-ignore: leave `displayName` be.
  604. ) => plugin.constructor.displayName || plugin.constructor.name;
  605. const getPluginKey = (plugin, globalConfig) => {
  606. if (typeof plugin.getUsageInfo === 'function') {
  607. return (
  608. plugin.getUsageInfo(globalConfig) || {
  609. key: null
  610. }
  611. ).key;
  612. }
  613. return null;
  614. };
  615. const usage = (globalConfig, watchPlugins, delimiter = '\n') => {
  616. const messages = [
  617. (0, _active_filters_message.default)(globalConfig),
  618. globalConfig.testPathPattern || globalConfig.testNamePattern
  619. ? _chalk().default.dim(' \u203A Press ') +
  620. 'c' +
  621. _chalk().default.dim(' to clear filters.')
  622. : null,
  623. '\n' + _chalk().default.bold('Watch Usage'),
  624. globalConfig.watch
  625. ? _chalk().default.dim(' \u203A Press ') +
  626. 'a' +
  627. _chalk().default.dim(' to run all tests.')
  628. : null,
  629. globalConfig.onlyFailures
  630. ? _chalk().default.dim(' \u203A Press ') +
  631. 'f' +
  632. _chalk().default.dim(' to quit "only failed tests" mode.')
  633. : _chalk().default.dim(' \u203A Press ') +
  634. 'f' +
  635. _chalk().default.dim(' to run only failed tests.'),
  636. (globalConfig.watchAll ||
  637. globalConfig.testPathPattern ||
  638. globalConfig.testNamePattern) &&
  639. !globalConfig.noSCM
  640. ? _chalk().default.dim(' \u203A Press ') +
  641. 'o' +
  642. _chalk().default.dim(' to only run tests related to changed files.')
  643. : null,
  644. ...(0, _watch_plugins_helpers.getSortedUsageRows)(
  645. watchPlugins,
  646. globalConfig
  647. ).map(
  648. plugin =>
  649. _chalk().default.dim(' \u203A Press') +
  650. ' ' +
  651. plugin.key +
  652. ' ' +
  653. _chalk().default.dim(`to ${plugin.prompt}.`)
  654. ),
  655. _chalk().default.dim(' \u203A Press ') +
  656. 'Enter' +
  657. _chalk().default.dim(' to trigger a test run.')
  658. ];
  659. return messages.filter(message => !!message).join(delimiter) + '\n';
  660. };
  661. const showToggleUsagePrompt = () =>
  662. '\n' +
  663. _chalk().default.bold('Watch Usage: ') +
  664. _chalk().default.dim('Press ') +
  665. 'w' +
  666. _chalk().default.dim(' to show more.');