watch.js 19 KB

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