diff --git a/.babelrc b/.babelrc index 253e1b3..1249407 100644 --- a/.babelrc +++ b/.babelrc @@ -5,7 +5,7 @@ { "useBuiltIns": true, "targets": { - "node": "4" + "node": "6" } } ], diff --git a/package.json b/package.json index 1964af5..be63fc2 100644 --- a/package.json +++ b/package.json @@ -24,9 +24,8 @@ "format": "prettier --single-quote --trailing-comma all --write \"!(build)/**/*.js\"" }, "dependencies": { - "ansi-escapes": "^3.0.0", "chalk": "^2.3.1", - "lodash": "4.17.5", + "jest-watcher": "^23.1.0", "slash": "^1.0.0", "string-length": "^2.0.0", "strip-ansi": "^4.0.0" diff --git a/src/__tests__/__snapshots__/jest_prompt.test.js.snap b/src/__tests__/__snapshots__/jest_prompt.test.js.snap deleted file mode 100644 index 7e8f660..0000000 --- a/src/__tests__/__snapshots__/jest_prompt.test.js.snap +++ /dev/null @@ -1,48 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Renders when no files match 1`] = ` -" - - - pattern › no-matchx -[MOCK - cursorSavePosition] - - - Pattern matches no files -[MOCK - cursorTo(20, 5)] -[MOCK - cursorRestorePosition]" -`; - -exports[`Renders when there are many matches 1`] = ` -" - - - pattern › p.* -[MOCK - cursorSavePosition] - - - Pattern matches 3 files - - › path/to/file-1.js - - › path/to/file-2.js - - › path/to/file-3.js -[MOCK - cursorTo(14, 5)] -[MOCK - cursorRestorePosition]" -`; - -exports[`Renders when there is one match 1`] = ` -" - - - pattern › p.*1 -[MOCK - cursorSavePosition] - - - Pattern matches 1 file - - › path/to/file-1.js -[MOCK - cursorTo(15, 5)] -[MOCK - cursorRestorePosition]" -`; diff --git a/src/__tests__/jest_prompt.test.js b/src/__tests__/jest_prompt.test.js deleted file mode 100644 index 30491e5..0000000 --- a/src/__tests__/jest_prompt.test.js +++ /dev/null @@ -1,105 +0,0 @@ -import chalk from 'chalk'; - -jest.doMock('chalk', () => new chalk.constructor({ enabled: false })); -jest.mock('ansi-escapes', () => ({ - clearScreen: '[MOCK - clearScreen]', - cursorDown: (count = 1) => `[MOCK - cursorDown(${count})]`, - cursorHide: '[MOCK - cursorHide]', - cursorRestorePosition: '[MOCK - cursorRestorePosition]', - cursorSavePosition: '[MOCK - cursorSavePosition]', - cursorShow: '[MOCK - cursorShow]', - cursorTo: (x, y) => `[MOCK - cursorTo(${x}, ${y})]`, -})); - -beforeEach(() => { - // eslint-disable-next-line global-require - const utils = require('../lib/utils'); - jest.spyOn(utils, 'getTerminalWidth').mockImplementation(() => 80); -}); - -const FileNamePatternPrompt = require('../file_name_pattern_prompt').default; -const Prompt = require('../shared/Prompt').default; - -const jestPrompt = ({ stdin, stdout, searchSources }) => { - const prompt = new Prompt(); - const p = new FileNamePatternPrompt(stdout, prompt); - p.updateSearchSources(searchSources); - - const onkeypress = key => { - prompt.put(key); - }; - - stdin.on('data', onkeypress); - p.run( - () => { - stdin.removeListener('data', onkeypress); - }, - () => { - stdin.removeListener('data', onkeypress); - }, - ); -}; - -class MockStdin { - constructor() { - this._callbacks = []; - } - - on(evt, callback) { - this._callbacks.push(callback); - } - - emit(key) { - this._callbacks.forEach(cb => cb(key)); - } -} - -let stdin; -let stdout; -let searchSource; -const nextTick = () => new Promise(res => process.nextTick(res)); - -beforeEach(() => { - stdin = new MockStdin(); - stdout = { write: jest.fn() }; - searchSource = { - config: { rootDir: '/rootDir/' }, - testPaths: [ - '/rootDir/path/to/file-1.js', - '/rootDir/path/to/file-2.js', - '/rootDir/path/to/file-3.js', - ], - }; -}); - -const type = async str => { - str.split('').forEach(c => { - stdin.emit(c); - }); - - await nextTick(); -}; - -it('Renders when no files match', () => { - jestPrompt({ stdin, stdout, searchSources: [searchSource] }); - type('no-match'); - stdout.write.mockReset(); - type('x'); - expect(stdout.write.mock.calls.join('\n')).toMatchSnapshot(); -}); - -it('Renders when there is one match', () => { - jestPrompt({ stdin, stdout, searchSources: [searchSource] }); - type('p.*'); - stdout.write.mockReset(); - type('1'); - expect(stdout.write.mock.calls.join('\n')).toMatchSnapshot(); -}); - -it('Renders when there are many matches', () => { - jestPrompt({ stdin, stdout, searchSources: [searchSource] }); - type('p.'); - stdout.write.mockReset(); - type('*'); - expect(stdout.write.mock.calls.join('\n')).toMatchSnapshot(); -}); diff --git a/src/file_name_pattern_prompt.js b/src/file_name_pattern_prompt.js index 09a122a..aef9503 100644 --- a/src/file_name_pattern_prompt.js +++ b/src/file_name_pattern_prompt.js @@ -2,19 +2,21 @@ import chalk from 'chalk'; import stringLength from 'string-length'; +import { + Prompt, + PatternPrompt, + printPatternCaret, + printRestoredPatternCaret, +} from 'jest-watcher'; import { highlight, getTerminalWidth, trimAndFormatPath } from './lib/utils'; -import Prompt from './shared/Prompt'; import { formatTypeaheadSelection, printMore, - printPatternCaret, printPatternMatches, - printRestoredPatternCaret, printStartTyping, printTypeaheadItem, } from './shared/pattern_mode_helpers'; import scroll, { type ScrollOptions } from './shared/scroll'; -import PatternPrompt from './shared/pattern_prompt'; import type { ProjectConfig } from './types/Config'; export type SearchSources = Array<{| @@ -52,7 +54,7 @@ export default class FileNamePatternPrompt extends PatternPrompt { const width = getTerminalWidth(); const { start, end, index } = scroll(total, options); - prompt.setTypeaheadLength(total); + prompt.setPromptLength(total); matchedTests .slice(start, end) diff --git a/src/file_name_plugin.js b/src/file_name_plugin.js index dbb22ff..79529d1 100644 --- a/src/file_name_plugin.js +++ b/src/file_name_plugin.js @@ -1,6 +1,6 @@ // @flow -import Prompt from './shared/Prompt'; +import { Prompt } from 'jest-watcher'; import FileNamePatternPrompt, { type SearchSources, } from './file_name_pattern_prompt'; diff --git a/src/lib/__tests__/utils.test.js b/src/lib/__tests__/utils.test.js new file mode 100644 index 0000000..4b6fb30 --- /dev/null +++ b/src/lib/__tests__/utils.test.js @@ -0,0 +1,10 @@ +import stripAnsi from 'strip-ansi'; +import { trimAndFormatPath } from '../utils'; + +test('trimAndFormatPath', () => { + expect( + stripAnsi( + trimAndFormatPath(2, { cwd: '/hello/there' }, '/hello/there/to/you', 80), + ), + ).toEqual('to/you'); +}); diff --git a/src/shared/Prompt.js b/src/shared/Prompt.js deleted file mode 100644 index 5ee37f3..0000000 --- a/src/shared/Prompt.js +++ /dev/null @@ -1,102 +0,0 @@ -// @flow - -import type { ScrollOptions } from './scroll'; - -import { KEYS } from './constants'; - -export default class Prompt { - _entering: boolean; - _value: string; - _onChange: Function; - _onSuccess: Function; - _onCancel: Function; - _typeaheadOffset: number; - _typeaheadLength: number; - _typeaheadSelection: string | null; - - constructor() { - (this: any)._onResize = this._onResize.bind(this); - } - - _onResize() { - this._onChange(this._value); - } - - enter( - onChange: (pattern: string, options: ScrollOptions) => void, - onSuccess: Function, - onCancel: Function, - ) { - this._entering = true; - this._value = ''; - this._onSuccess = onSuccess; - this._onCancel = onCancel; - this._typeaheadSelection = null; - this._typeaheadOffset = -1; - this._typeaheadLength = 0; - this._onChange = () => - onChange(this._value, { - max: 10, - offset: this._typeaheadOffset, - }); - - this._onChange(); - - process.stdout.on('resize', this._onResize); - } - - setTypeaheadLength(length: number) { - this._typeaheadLength = length; - } - - setTypheadheadSelection(selected: string) { - this._typeaheadSelection = selected; - } - - put(key: string) { - switch (key) { - case KEYS.ENTER: - this._entering = false; - this._onSuccess(this._typeaheadSelection || this._value); - this.abort(); - break; - case KEYS.ESCAPE: - this._entering = false; - this._onCancel(this._value); - this.abort(); - break; - case KEYS.ARROW_DOWN: - this._typeaheadOffset = Math.min( - this._typeaheadOffset + 1, - this._typeaheadLength - 1, - ); - this._onChange(); - break; - case KEYS.ARROW_UP: - this._typeaheadOffset = Math.max(this._typeaheadOffset - 1, -1); - this._onChange(); - break; - case KEYS.ARROW_LEFT: - case KEYS.ARROW_RIGHT: - break; - default: { - this._value = - key === KEYS.BACKSPACE ? this._value.slice(0, -1) : this._value + key; - this._typeaheadOffset = -1; - this._typeaheadSelection = null; - this._onChange(); - break; - } - } - } - - abort() { - this._entering = false; - this._value = ''; - process.stdout.removeListener('resize', this._onResize); - } - - isEntering() { - return this._entering; - } -} diff --git a/src/shared/constants.js b/src/shared/constants.js deleted file mode 100644 index 896969f..0000000 --- a/src/shared/constants.js +++ /dev/null @@ -1,38 +0,0 @@ -// @flow - -const isWindows = process.platform === 'win32'; - -export const CLEAR = isWindows ? '\x1Bc' : '\x1B[2J\x1B[3J\x1B[H'; - -export const KEYS = { - A: 'a', - ARROW_DOWN: '\u001b[B', - ARROW_LEFT: '\u001b[D', - ARROW_RIGHT: '\u001b[C', - ARROW_UP: '\u001b[A', - BACKSPACE: isWindows - ? Buffer.from('08', 'hex').toString() - : Buffer.from('7f', 'hex').toString(), - C: 'c', - CONTROL_C: '\u0003', - CONTROL_D: '\u0004', - ENTER: '\r', - ESCAPE: '\u001b', - F: 'f', - I: 'i', - O: 'o', - P: 'p', - Q: 'q', - QUESTION_MARK: '?', - R: 'r', - S: 's', - T: 't', - U: 'u', - W: 'w', -}; - -export const ICONS = { - failed: isWindows ? '\u00D7' : '\u2715', - pending: '\u25CB', - success: isWindows ? '\u221A' : '\u2713', -}; diff --git a/src/shared/pattern_mode_helpers.js b/src/shared/pattern_mode_helpers.js index 91af4eb..70d4ffa 100644 --- a/src/shared/pattern_mode_helpers.js +++ b/src/shared/pattern_mode_helpers.js @@ -1,10 +1,8 @@ // @flow import chalk from 'chalk'; -import ansiEscapes from 'ansi-escapes'; -import stringLength from 'string-length'; import stripAnsi from 'strip-ansi'; -import Prompt from '../shared/Prompt'; +import { Prompt } from 'jest-watcher'; const pluralize = (count: number, text: string) => count === 1 ? text : `${text}s`; @@ -23,30 +21,6 @@ export const printPatternMatches = ( pipe.write(result + extraText); }; -export const printPatternCaret = ( - pattern: string, - pipe: stream$Writable | tty$WriteStream, -) => { - const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; - - pipe.write(ansiEscapes.eraseDown); - pipe.write(inputText); - pipe.write(ansiEscapes.cursorSavePosition); -}; - -export const printRestoredPatternCaret = ( - pattern: string, - currentUsageRows: number, - pipe: stream$Writable | tty$WriteStream, -) => { - const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; - - pipe.write( - ansiEscapes.cursorTo(stringLength(inputText), currentUsageRows - 1), - ); - pipe.write(ansiEscapes.cursorRestorePosition); -}; - export const printStartTyping = ( entity: string, pipe: stream$Writable | tty$WriteStream, @@ -80,7 +54,7 @@ export const formatTypeaheadSelection = ( prompt: Prompt, ) => { if (index === activeIndex) { - prompt.setTypheadheadSelection(stripAnsi(item)); + prompt.setPromptSelection(stripAnsi(item)); return chalk.black.bgYellow(stripAnsi(item)); } return item; diff --git a/src/shared/pattern_prompt.js b/src/shared/pattern_prompt.js deleted file mode 100644 index fff8cb0..0000000 --- a/src/shared/pattern_prompt.js +++ /dev/null @@ -1,52 +0,0 @@ -// @flow - -import chalk from 'chalk'; -import ansiEscapes from 'ansi-escapes'; -import Prompt from './Prompt'; -import { type ScrollOptions } from './scroll'; - -const usage = (entity: string) => - // eslint-disable-nextline prefer-template - `\n${chalk.bold('Pattern Mode Usage')}\n` + - ` ${chalk.dim('\u203A Press')} Esc ${chalk.dim('to exit pattern mode.')}\n` + - ` ${chalk.dim('\u203A Press')} Enter ` + - `${chalk.dim(`to apply pattern to all ${entity}.`)}\n` + - `\n`; - -const usageRows = usage('').split('\n').length; - -export default class PatternPrompt { - _pipe: stream$Writable | tty$WriteStream; - _prompt: Prompt; - _entityName: string; - _currentUsageRows: number; - - constructor(pipe: stream$Writable | tty$WriteStream, prompt: Prompt) { - this._pipe = pipe; - this._prompt = prompt; - this._currentUsageRows = usageRows; - } - - run(onSuccess: Function, onCancel: Function, options?: { header: string }) { - this._pipe.write(ansiEscapes.cursorHide); - this._pipe.write(ansiEscapes.clearScreen); - - if (options && options.header) { - this._pipe.write(`${options.header}\n`); - this._currentUsageRows = usageRows + options.header.split('\n').length; - } else { - this._currentUsageRows = usageRows; - } - - this._pipe.write(usage(this._entityName)); - this._pipe.write(ansiEscapes.cursorShow); - - this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel); - } - - // eslint-disable-next-line no-unused-vars - _onChange(pattern: string, options: ScrollOptions) { - this._pipe.write(ansiEscapes.eraseLine); - this._pipe.write(ansiEscapes.cursorLeft); - } -} diff --git a/src/test_name_pattern_prompt.js b/src/test_name_pattern_prompt.js index 50db758..ddb2823 100644 --- a/src/test_name_pattern_prompt.js +++ b/src/test_name_pattern_prompt.js @@ -1,19 +1,21 @@ // @flow import chalk from 'chalk'; +import { + Prompt, + PatternPrompt, + printPatternCaret, + printRestoredPatternCaret, +} from 'jest-watcher'; import scroll, { type ScrollOptions } from './shared/scroll'; import { formatTestNameByPattern, getTerminalWidth } from './lib/utils'; -import Prompt from './shared/Prompt'; import { formatTypeaheadSelection, printMore, - printPatternCaret, printPatternMatches, - printRestoredPatternCaret, printStartTyping, printTypeaheadItem, } from './shared/pattern_mode_helpers'; -import PatternPrompt from './shared/pattern_prompt'; export type TestResult = { testResults: Array<{ @@ -54,7 +56,7 @@ class TestNamePatternPrompt extends PatternPrompt { const width = getTerminalWidth(); const { start, end, index } = scroll(total, options); - prompt.setTypeaheadLength(total); + prompt.setPromptLength(total); matchedTests .slice(start, end) diff --git a/src/test_name_plugin.js b/src/test_name_plugin.js index 69b3665..5f5df30 100644 --- a/src/test_name_plugin.js +++ b/src/test_name_plugin.js @@ -1,6 +1,6 @@ // @flow -import Prompt from './shared/Prompt'; +import { Prompt } from 'jest-watcher'; import TestNamePatternPrompt, { type TestResult, } from './test_name_pattern_prompt'; diff --git a/yarn.lock b/yarn.lock index 099a3d5..272370a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1010,10 +1010,6 @@ cliui@^4.0.0: strip-ansi "^4.0.0" wrap-ansi "^2.0.0" -clorox@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clorox/-/clorox-1.0.2.tgz#f89dbaba26046f7327dfabc2dd97e76af91d742d" - co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -2369,6 +2365,14 @@ jest-validate@^22.4.2: leven "^2.1.0" pretty-format "^22.4.0" +jest-watcher@^23.1.0: + version "23.1.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.1.0.tgz#a8d5842e38d9fb4afff823df6abb42a58ae6cdbd" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.1" + string-length "^2.0.0" + jest-worker@^22.2.2: version "22.2.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-22.2.2.tgz#c1f5dc39976884b81f68ec50cb8532b2cbab3390" @@ -2546,7 +2550,7 @@ lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" -lodash@4.17.5, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0: +lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0: version "4.17.5" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" @@ -2974,13 +2978,6 @@ progress@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" -"prompts@git://github.com/terkelg/prompts.git#792824f8e5bfe3d632da0e48be23ab718b8f6646": - version "0.1.4" - resolved "git://github.com/terkelg/prompts.git#792824f8e5bfe3d632da0e48be23ab718b8f6646" - dependencies: - clorox "^1.0.1" - sisteransi "^0.1.0" - pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" @@ -3330,10 +3327,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" -sisteransi@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.0.tgz#2e6706ac427019b84e60f751d588d87920484f25" - slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"