watch.js 19 KB

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