From 3d6f27e39e32f92d5204791df832df2ca34f80fb Mon Sep 17 00:00:00 2001 From: Rogelio Guzman Date: Thu, 27 Apr 2017 10:54:31 -0700 Subject: [PATCH 01/11] Add typeahead arrow selection --- .../jest-cli/src/TestNamePatternPrompt.js | 27 +++++++++------ .../jest-cli/src/TestPathPatternPrompt.js | 19 ++++++++--- packages/jest-cli/src/lib/Prompt.js | 26 ++++++++++---- .../jest-cli/src/lib/__tests__/Prompt-test.js | 11 +++--- packages/jest-cli/src/lib/scroll-list.js | 34 +++++++++++++++++++ 5 files changed, 92 insertions(+), 25 deletions(-) create mode 100644 packages/jest-cli/src/lib/scroll-list.js diff --git a/packages/jest-cli/src/TestNamePatternPrompt.js b/packages/jest-cli/src/TestNamePatternPrompt.js index f79f68d899af..3d6f96c60093 100644 --- a/packages/jest-cli/src/TestNamePatternPrompt.js +++ b/packages/jest-cli/src/TestNamePatternPrompt.js @@ -14,6 +14,7 @@ import type {TestResult} from 'types/TestResult'; const ansiEscapes = require('ansi-escapes'); const chalk = require('chalk'); +const scroll = require('./lib/scroll-list'); const {getTerminalWidth} = require('./lib/terminalUtils'); const stringLength = require('string-length'); const Prompt = require('./lib/Prompt'); @@ -57,17 +58,17 @@ module.exports = class TestNamePatternPrompt { this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel); } - _onChange(pattern: string) { + _onChange(pattern: string, options) { this._pipe.write(ansiEscapes.eraseLine); this._pipe.write(ansiEscapes.cursorLeft); - this._printTypeahead(pattern, 10); + this._printTypeahead(pattern, Object.assign({max: 10}, options)); } - _printTypeahead(pattern: string, max: number) { + _printTypeahead(pattern: string, options) { + const { max } = options; const matchedTests = this._getMatchedTests(pattern); const total = matchedTests.length; - const results = matchedTests.slice(0, max); const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; this._pipe.write(ansiEscapes.eraseDown); @@ -86,12 +87,18 @@ module.exports = class TestNamePatternPrompt { this._pipe.write(' from cached test suites.'); const width = getTerminalWidth(); - - results.forEach(name => { - const testName = formatTestNameByPattern(name, pattern, width - 4); - - this._pipe.write(`\n ${chalk.dim('\u203A')} ${testName}`); - }); + const { start, end, index } = scroll(matchedTests.length, options); + + matchedTests.slice(start, end) + .map(name => formatTestNameByPattern(name, pattern, width - 4)) + .map((item, i) => { + if (i === index) { + this._prompt.setSelected('^' + chalk.stripColor(item) + '$'); + return chalk.black.bgYellow(chalk.stripColor(item)); + } + return item; + }) + .forEach(output => this._pipe.write(`\n ${chalk.dim('\u203A')} ${output}`)); if (total > max) { const more = total - max; diff --git a/packages/jest-cli/src/TestPathPatternPrompt.js b/packages/jest-cli/src/TestPathPatternPrompt.js index 0fce3466ba93..3d42f30ca1b2 100644 --- a/packages/jest-cli/src/TestPathPatternPrompt.js +++ b/packages/jest-cli/src/TestPathPatternPrompt.js @@ -16,6 +16,7 @@ import type SearchSource from './SearchSource'; const ansiEscapes = require('ansi-escapes'); const chalk = require('chalk'); +const scroll = require('./lib/scroll-list'); const {getTerminalWidth} = require('./lib/terminalUtils'); const highlight = require('./lib/highlight'); const stringLength = require('string-length'); @@ -65,7 +66,7 @@ module.exports = class TestPathPatternPrompt { this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel); } - _onChange(pattern: string) { + _onChange(pattern: string, options) { let regex; try { @@ -81,10 +82,11 @@ module.exports = class TestPathPatternPrompt { this._pipe.write(ansiEscapes.eraseLine); this._pipe.write(ansiEscapes.cursorLeft); - this._printTypeahead(pattern, tests, 10); + this._printTypeahead(pattern, tests, Object.assign({max: 10}, options)); } - _printTypeahead(pattern: string, allResults: Array, max: number) { + _printTypeahead(pattern: string, allResults: Array, options) { + const { max } = options; const total = allResults.length; const results = allResults.slice(0, max); const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; @@ -106,7 +108,9 @@ module.exports = class TestPathPatternPrompt { const prefix = ` ${chalk.dim('\u203A')} `; const padding = stringLength(prefix) + 2; - results + const { start, end, index } = scroll(allResults.length, options); + + allResults.slice(start, end) .map(({path, context}) => { const filePath = trimAndFormatPath( padding, @@ -116,6 +120,13 @@ module.exports = class TestPathPatternPrompt { ); return highlight(path, filePath, pattern, context.config.rootDir); }) + .map((item, i) => { + if (i === index) { + this._prompt.setSelected(chalk.stripColor(item)); + return chalk.black.bgYellow(chalk.stripColor(item)); + } + return item; + }) .forEach(filePath => this._pipe.write(`\n ${chalk.dim('\u203A')} ${filePath}`), ); diff --git a/packages/jest-cli/src/lib/Prompt.js b/packages/jest-cli/src/lib/Prompt.js index 255b19c6027d..e9a0a275e157 100644 --- a/packages/jest-cli/src/lib/Prompt.js +++ b/packages/jest-cli/src/lib/Prompt.js @@ -18,6 +18,8 @@ class Prompt { _onChange: Function; _onSuccess: Function; _onCancel: Function; + _offset: number; + _selected: string|null; constructor() { (this: any)._onResize = this._onResize.bind(this); @@ -30,20 +32,25 @@ class Prompt { enter(onChange: Function, onSuccess: Function, onCancel: Function) { this._entering = true; this._value = ''; - this._onChange = onChange; this._onSuccess = onSuccess; this._onCancel = onCancel; + this._offset = -1; + this._onChange = () => onChange(this._value, { offset: this._offset }); - onChange(this._value); + this._onChange(); process.stdout.on('resize', this._onResize); } + setSelected(selected: string) { + this._selected = selected; + } + put(key: string) { switch (key) { case KEYS.ENTER: this._entering = false; - this._onSuccess(this._value); + this._onSuccess(this._selected || this._value); this.abort(); break; case KEYS.ESCAPE: @@ -52,9 +59,15 @@ class Prompt { this.abort(); break; case KEYS.ARROW_DOWN: + this._offset += 1; + this._onChange(); + break; + case KEYS.ARROW_UP: + this._offset = Math.max(this._offset - 1, -1); + this._onChange(); + break; case KEYS.ARROW_LEFT: case KEYS.ARROW_RIGHT: - case KEYS.ARROW_UP: break; default: const char = new Buffer(key, 'hex').toString(); @@ -62,8 +75,9 @@ class Prompt { this._value = key === KEYS.BACKSPACE ? this._value.slice(0, -1) : this._value + char; - - this._onChange(this._value); + this._offset = -1; + this._selected = null; + this._onChange(); break; } } diff --git a/packages/jest-cli/src/lib/__tests__/Prompt-test.js b/packages/jest-cli/src/lib/__tests__/Prompt-test.js index 47913f85c96f..c10cdf18afed 100644 --- a/packages/jest-cli/src/lib/__tests__/Prompt-test.js +++ b/packages/jest-cli/src/lib/__tests__/Prompt-test.js @@ -18,24 +18,25 @@ KEYS = Object.assign({}, KEYS, { }); it('calls handler on change value', () => { + const options = {offset: -1}; const prompt = new Prompt(); const onChange = jest.fn(); prompt.enter(onChange, jest.fn(), jest.fn()); - expect(onChange).toHaveBeenLastCalledWith(''); + expect(onChange).toHaveBeenLastCalledWith('', options); prompt.put(KEYS.T); - expect(onChange).toHaveBeenLastCalledWith('t'); + expect(onChange).toHaveBeenLastCalledWith('t', options); prompt.put(KEYS.E); - expect(onChange).toHaveBeenLastCalledWith('te'); + expect(onChange).toHaveBeenLastCalledWith('te', options); prompt.put(KEYS.S); - expect(onChange).toHaveBeenLastCalledWith('tes'); + expect(onChange).toHaveBeenLastCalledWith('tes', options); prompt.put(KEYS.T); - expect(onChange).toHaveBeenLastCalledWith('test'); + expect(onChange).toHaveBeenLastCalledWith('test', options); expect(onChange).toHaveBeenCalledTimes(5); }); diff --git a/packages/jest-cli/src/lib/scroll-list.js b/packages/jest-cli/src/lib/scroll-list.js new file mode 100644 index 000000000000..0b76cad80033 --- /dev/null +++ b/packages/jest-cli/src/lib/scroll-list.js @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ + +'use strict'; + +const scroll = (size, { offset, max }) => { + let start = 0; + let index = Math.min(offset, size); + + const halfScreen = max / 2; + + if (index <= halfScreen) { + start = 0; + } else { + start = Math.min(index - halfScreen - 1, size - max); + index = Math.min(index - start, size); + } + + return { + end: Math.min(size, start + max), + index, + start, + }; +} + + +module.exports = scroll; From 0bfdbf09ca4f5556a7f839ba57a32a0c8dd02aa2 Mon Sep 17 00:00:00 2001 From: Rogelio Guzman Date: Thu, 27 Apr 2017 11:17:25 -0700 Subject: [PATCH 02/11] Handle max typeahead max offset --- .../jest-cli/src/TestNamePatternPrompt.js | 13 +++++--- .../jest-cli/src/TestPathPatternPrompt.js | 10 +++--- packages/jest-cli/src/lib/Prompt.js | 32 ++++++++++++++----- packages/jest-cli/src/lib/scroll-list.js | 7 +++- 4 files changed, 44 insertions(+), 18 deletions(-) diff --git a/packages/jest-cli/src/TestNamePatternPrompt.js b/packages/jest-cli/src/TestNamePatternPrompt.js index 3d6f96c60093..14d4251bcd88 100644 --- a/packages/jest-cli/src/TestNamePatternPrompt.js +++ b/packages/jest-cli/src/TestNamePatternPrompt.js @@ -11,6 +11,7 @@ 'use strict'; import type {TestResult} from 'types/TestResult'; +import type {ScrollOptions} from './lib/scroll-list'; const ansiEscapes = require('ansi-escapes'); const chalk = require('chalk'); @@ -58,13 +59,13 @@ module.exports = class TestNamePatternPrompt { this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel); } - _onChange(pattern: string, options) { + _onChange(pattern: string, options: ScrollOptions) { this._pipe.write(ansiEscapes.eraseLine); this._pipe.write(ansiEscapes.cursorLeft); - this._printTypeahead(pattern, Object.assign({max: 10}, options)); + this._printTypeahead(pattern, options); } - _printTypeahead(pattern: string, options) { + _printTypeahead(pattern: string, options: ScrollOptions) { const { max } = options; const matchedTests = this._getMatchedTests(pattern); @@ -87,13 +88,15 @@ module.exports = class TestNamePatternPrompt { this._pipe.write(' from cached test suites.'); const width = getTerminalWidth(); - const { start, end, index } = scroll(matchedTests.length, options); + const { start, end, index } = scroll(total, options); + + this._prompt.setTypeaheadLength(total); matchedTests.slice(start, end) .map(name => formatTestNameByPattern(name, pattern, width - 4)) .map((item, i) => { if (i === index) { - this._prompt.setSelected('^' + chalk.stripColor(item) + '$'); + this._prompt.setSelected(chalk.stripColor(item)); return chalk.black.bgYellow(chalk.stripColor(item)); } return item; diff --git a/packages/jest-cli/src/TestPathPatternPrompt.js b/packages/jest-cli/src/TestPathPatternPrompt.js index 3d42f30ca1b2..aee783c3fdb6 100644 --- a/packages/jest-cli/src/TestPathPatternPrompt.js +++ b/packages/jest-cli/src/TestPathPatternPrompt.js @@ -12,6 +12,7 @@ import type {Context} from 'types/Context'; import type {Test} from 'types/TestRunner'; +import type {ScrollOptions} from './lib/scroll-list'; import type SearchSource from './SearchSource'; const ansiEscapes = require('ansi-escapes'); @@ -66,7 +67,7 @@ module.exports = class TestPathPatternPrompt { this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel); } - _onChange(pattern: string, options) { + _onChange(pattern: string, options: ScrollOptions) { let regex; try { @@ -82,10 +83,10 @@ module.exports = class TestPathPatternPrompt { this._pipe.write(ansiEscapes.eraseLine); this._pipe.write(ansiEscapes.cursorLeft); - this._printTypeahead(pattern, tests, Object.assign({max: 10}, options)); + this._printTypeahead(pattern, tests, options); } - _printTypeahead(pattern: string, allResults: Array, options) { + _printTypeahead(pattern: string, allResults: Array, options: ScrollOptions) { const { max } = options; const total = allResults.length; const results = allResults.slice(0, max); @@ -108,8 +109,9 @@ module.exports = class TestPathPatternPrompt { const prefix = ` ${chalk.dim('\u203A')} `; const padding = stringLength(prefix) + 2; - const { start, end, index } = scroll(allResults.length, options); + const { start, end, index } = scroll(total, options); + this._prompt.setTypeaheadLength(total); allResults.slice(start, end) .map(({path, context}) => { const filePath = trimAndFormatPath( diff --git a/packages/jest-cli/src/lib/Prompt.js b/packages/jest-cli/src/lib/Prompt.js index e9a0a275e157..6868c1f82f29 100644 --- a/packages/jest-cli/src/lib/Prompt.js +++ b/packages/jest-cli/src/lib/Prompt.js @@ -10,15 +10,19 @@ 'use strict'; +import type {ScrollOptions} from './scroll-list'; + const {KEYS} = require('../constants'); + class Prompt { _entering: boolean; _value: string; - _onChange: Function; + _onChange: (Function); _onSuccess: Function; _onCancel: Function; - _offset: number; + _typeaheadOffset: number; + _typeaheadLength: number; _selected: string|null; constructor() { @@ -29,19 +33,31 @@ class Prompt { this._onChange(this._value); } - enter(onChange: Function, onSuccess: Function, onCancel: Function) { + enter( + onChange: (pattern: string, options: ScrollOptions) => void, + onSuccess: Function, + onCancel: Function, + ) { this._entering = true; this._value = ''; this._onSuccess = onSuccess; this._onCancel = onCancel; - this._offset = -1; - this._onChange = () => onChange(this._value, { offset: this._offset }); + 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; + } + setSelected(selected: string) { this._selected = selected; } @@ -59,11 +75,11 @@ class Prompt { this.abort(); break; case KEYS.ARROW_DOWN: - this._offset += 1; + this._typeaheadOffset = Math.min(this._typeaheadOffset + 1, this._typeaheadLength - 1); this._onChange(); break; case KEYS.ARROW_UP: - this._offset = Math.max(this._offset - 1, -1); + this._typeaheadOffset = Math.max(this._typeaheadOffset - 1, -1); this._onChange(); break; case KEYS.ARROW_LEFT: @@ -75,7 +91,7 @@ class Prompt { this._value = key === KEYS.BACKSPACE ? this._value.slice(0, -1) : this._value + char; - this._offset = -1; + this._typeaheadOffset = -1; this._selected = null; this._onChange(); break; diff --git a/packages/jest-cli/src/lib/scroll-list.js b/packages/jest-cli/src/lib/scroll-list.js index 0b76cad80033..d614409f9821 100644 --- a/packages/jest-cli/src/lib/scroll-list.js +++ b/packages/jest-cli/src/lib/scroll-list.js @@ -10,7 +10,12 @@ 'use strict'; -const scroll = (size, { offset, max }) => { +export type ScrollOptions = { + offset: number, + max: number, +} + +const scroll = (size: number, { offset, max }: ScrollOptions) => { let start = 0; let index = Math.min(offset, size); From 0ed7001e158da8c100b0f715473f8c255a323c3d Mon Sep 17 00:00:00 2001 From: Rogelio Guzman Date: Thu, 27 Apr 2017 12:37:35 -0700 Subject: [PATCH 03/11] Add tests for scroll list --- .../jest-cli/src/TestNamePatternPrompt.js | 4 +- .../jest-cli/src/TestPathPatternPrompt.js | 4 +- packages/jest-cli/src/lib/Prompt.js | 2 +- .../src/lib/__tests__/scrollList-test.js | 57 +++++++++++++++++++ .../src/lib/{scroll-list.js => scrollList.js} | 0 5 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 packages/jest-cli/src/lib/__tests__/scrollList-test.js rename packages/jest-cli/src/lib/{scroll-list.js => scrollList.js} (100%) diff --git a/packages/jest-cli/src/TestNamePatternPrompt.js b/packages/jest-cli/src/TestNamePatternPrompt.js index 14d4251bcd88..8002a5165bb1 100644 --- a/packages/jest-cli/src/TestNamePatternPrompt.js +++ b/packages/jest-cli/src/TestNamePatternPrompt.js @@ -11,11 +11,11 @@ 'use strict'; import type {TestResult} from 'types/TestResult'; -import type {ScrollOptions} from './lib/scroll-list'; +import type {ScrollOptions} from './lib/scrollList'; const ansiEscapes = require('ansi-escapes'); const chalk = require('chalk'); -const scroll = require('./lib/scroll-list'); +const scroll = require('./lib/scrollList'); const {getTerminalWidth} = require('./lib/terminalUtils'); const stringLength = require('string-length'); const Prompt = require('./lib/Prompt'); diff --git a/packages/jest-cli/src/TestPathPatternPrompt.js b/packages/jest-cli/src/TestPathPatternPrompt.js index aee783c3fdb6..2ec5312369f8 100644 --- a/packages/jest-cli/src/TestPathPatternPrompt.js +++ b/packages/jest-cli/src/TestPathPatternPrompt.js @@ -12,12 +12,12 @@ import type {Context} from 'types/Context'; import type {Test} from 'types/TestRunner'; -import type {ScrollOptions} from './lib/scroll-list'; +import type {ScrollOptions} from './lib/scrollList'; import type SearchSource from './SearchSource'; const ansiEscapes = require('ansi-escapes'); const chalk = require('chalk'); -const scroll = require('./lib/scroll-list'); +const scroll = require('./lib/scrollList'); const {getTerminalWidth} = require('./lib/terminalUtils'); const highlight = require('./lib/highlight'); const stringLength = require('string-length'); diff --git a/packages/jest-cli/src/lib/Prompt.js b/packages/jest-cli/src/lib/Prompt.js index 6868c1f82f29..3a1791535a6c 100644 --- a/packages/jest-cli/src/lib/Prompt.js +++ b/packages/jest-cli/src/lib/Prompt.js @@ -10,7 +10,7 @@ 'use strict'; -import type {ScrollOptions} from './scroll-list'; +import type {ScrollOptions} from './scrollList'; const {KEYS} = require('../constants'); diff --git a/packages/jest-cli/src/lib/__tests__/scrollList-test.js b/packages/jest-cli/src/lib/__tests__/scrollList-test.js new file mode 100644 index 000000000000..8a47f12b0bde --- /dev/null +++ b/packages/jest-cli/src/lib/__tests__/scrollList-test.js @@ -0,0 +1,57 @@ +const scroll = require('../scrollList'); + +it('When offset is -1', () => { + expect(scroll(25, { offset: -1, max: 10 })).toEqual({ + end: 10, + index: -1, + start: 0, + }); +}); + +it('When offset is in the first set of items', () => { + expect(scroll(25, { offset: 4, max: 10 })).toEqual({ + end: 10, + index: 4, + start: 0, + }); + + expect(scroll(25, { offset: 6, max: 10 })).toEqual({ + end: 10, + index: 6, + start: 0, + }); +}); + +it('When offset is in the middle of the list', () => { + expect(scroll(25, { offset: 7, max: 10 })).toEqual({ + end: 11, + index: 6, + start: 1, + }); + + expect(scroll(25, { offset: 14, max: 10 })).toEqual({ + end: 18, + index: 6, + start: 8, + }); +}); + +it('When offset is at the end of the list', () => { + expect(scroll(25, { offset: 23, max: 10 })).toEqual({ + end: 25, + index: 8, + start: 15, + }); + + expect(scroll(25, { offset: 25, max: 10 })).toEqual({ + end: 25, + index: 10, + start: 15, + }); + + expect(scroll(25, { offset: 35, max: 10 })).toEqual({ + end: 25, + index: 10, + start: 15, + }); +}); diff --git a/packages/jest-cli/src/lib/scroll-list.js b/packages/jest-cli/src/lib/scrollList.js similarity index 100% rename from packages/jest-cli/src/lib/scroll-list.js rename to packages/jest-cli/src/lib/scrollList.js From 3d9991eb62cdde25bd5eb44c159ba112492bce2f Mon Sep 17 00:00:00 2001 From: Rogelio Guzman Date: Thu, 27 Apr 2017 12:52:19 -0700 Subject: [PATCH 04/11] Add simple tests to typeheahead selection --- .../watch-filename-pattern-mode-test.js.snap | 2 ++ .../watch-test-name-pattern-mode-test.js.snap | 2 ++ .../watch-filename-pattern-mode-test.js | 18 +++++++++++++++ .../watch-test-name-pattern-mode-test.js | 22 +++++++++++++++++-- 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap index 7959805141a8..dd5740b852f0 100644 --- a/packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap +++ b/packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap @@ -459,3 +459,5 @@ Pattern Mode Usage [MOCK - cursorTo(11, 7)] [MOCK - cursorRestorePosition]" `; + +exports[`Watch mode flows can select a specific file name from the typeahead results 1`] = `"path/to/file2-test.js"`; diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap index f105f90b65dd..c7c90b75c991 100644 --- a/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap +++ b/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap @@ -239,3 +239,5 @@ exports[`Watch mode flows Results in pattern mode get truncated appropriately 2` [MOCK - cursorTo(12, 5)] [MOCK - cursorRestorePosition]" `; + +exports[`Watch mode flows can select a specific test name from the typeahead results 1`] = `"should convert string to a RegExp"`; diff --git a/packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js b/packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js index 0588a76b86c9..03f68a501409 100644 --- a/packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js +++ b/packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js @@ -144,6 +144,24 @@ describe('Watch mode flows', () => { }); }); + it('can select a specific file name from the typeahead results', () => { + contexts[0].config = {rootDir: ''}; + watch(globalConfig, contexts, argv, pipe, hasteMapInstances, stdin); + + // Write a enter pattern mode + stdin.emit(KEYS.P); + + // Write a pattern + ['p', '.', '*'] + .map(toHex) + .concat([KEYS.ARROW_DOWN, KEYS.ARROW_DOWN, KEYS.ARROW_DOWN, KEYS.ARROW_UP]) + .forEach(key => stdin.emit(key)); + + stdin.emit(KEYS.ENTER); + + expect(argv.testPathPattern).toMatchSnapshot(); + }); + it('Results in pattern mode get truncated appropriately', () => { contexts[0].config = {rootDir: ''}; watch(globalConfig, contexts, argv, pipe, hasteMapInstances, stdin); diff --git a/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js b/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js index 6ef1a42b06ef..de9abb7ce6ad 100644 --- a/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js +++ b/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js @@ -101,6 +101,8 @@ jest.doMock('../lib/terminalUtils', () => ({ const watch = require('../watch'); +const toHex = char => Number(char.charCodeAt(0)).toString(16); + const globalConfig = { watch: true, }; @@ -137,8 +139,6 @@ describe('Watch mode flows', () => { expect(pipe.write.mock.calls.join('\n')).toMatchSnapshot(); }; - const toHex = char => Number(char.charCodeAt(0)).toString(16); - // Write a pattern ['c', 'o', 'n', ' ', '1', '2'].map(toHex).forEach(assertPattern); @@ -160,6 +160,24 @@ describe('Watch mode flows', () => { }); }); + it('can select a specific test name from the typeahead results', () => { + contexts[0].config = {rootDir: ''}; + watch(globalConfig, contexts, argv, pipe, hasteMapInstances, stdin); + + // Write a enter pattern mode + stdin.emit(KEYS.T); + + // Write a pattern + ['c', 'o', 'n'] + .map(toHex) + .concat([KEYS.ARROW_DOWN, KEYS.ARROW_DOWN, KEYS.ARROW_DOWN, KEYS.ARROW_UP]) + .forEach(key => stdin.emit(key)); + + stdin.emit(KEYS.ENTER); + + expect(argv.testNamePattern).toMatchSnapshot(); + }); + it('Results in pattern mode get truncated appropriately', () => { contexts[0].config = {rootDir: ''}; watch(globalConfig, contexts, argv, pipe, hasteMapInstances, stdin); From 8aa6fb968483f684c7b738eb47a4f9d729b55bc1 Mon Sep 17 00:00:00 2001 From: Rogelio Guzman Date: Thu, 27 Apr 2017 12:56:19 -0700 Subject: [PATCH 05/11] Clear typeahead selection on enter --- packages/jest-cli/src/TestNamePatternPrompt.js | 2 +- packages/jest-cli/src/TestPathPatternPrompt.js | 2 +- packages/jest-cli/src/lib/Prompt.js | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/jest-cli/src/TestNamePatternPrompt.js b/packages/jest-cli/src/TestNamePatternPrompt.js index 8002a5165bb1..c7c2a66b3ff4 100644 --- a/packages/jest-cli/src/TestNamePatternPrompt.js +++ b/packages/jest-cli/src/TestNamePatternPrompt.js @@ -96,7 +96,7 @@ module.exports = class TestNamePatternPrompt { .map(name => formatTestNameByPattern(name, pattern, width - 4)) .map((item, i) => { if (i === index) { - this._prompt.setSelected(chalk.stripColor(item)); + this._prompt.setTypheadheadSelection(chalk.stripColor(item)); return chalk.black.bgYellow(chalk.stripColor(item)); } return item; diff --git a/packages/jest-cli/src/TestPathPatternPrompt.js b/packages/jest-cli/src/TestPathPatternPrompt.js index 2ec5312369f8..27947d1d1a25 100644 --- a/packages/jest-cli/src/TestPathPatternPrompt.js +++ b/packages/jest-cli/src/TestPathPatternPrompt.js @@ -124,7 +124,7 @@ module.exports = class TestPathPatternPrompt { }) .map((item, i) => { if (i === index) { - this._prompt.setSelected(chalk.stripColor(item)); + this._prompt.setTypheadheadSelection(chalk.stripColor(item)); return chalk.black.bgYellow(chalk.stripColor(item)); } return item; diff --git a/packages/jest-cli/src/lib/Prompt.js b/packages/jest-cli/src/lib/Prompt.js index 3a1791535a6c..7510402d23f8 100644 --- a/packages/jest-cli/src/lib/Prompt.js +++ b/packages/jest-cli/src/lib/Prompt.js @@ -14,7 +14,6 @@ import type {ScrollOptions} from './scrollList'; const {KEYS} = require('../constants'); - class Prompt { _entering: boolean; _value: string; @@ -23,7 +22,7 @@ class Prompt { _onCancel: Function; _typeaheadOffset: number; _typeaheadLength: number; - _selected: string|null; + _typeaheadSelection: string|null; constructor() { (this: any)._onResize = this._onResize.bind(this); @@ -42,6 +41,7 @@ class Prompt { this._value = ''; this._onSuccess = onSuccess; this._onCancel = onCancel; + this._typeaheadSelection = null; this._typeaheadOffset = -1; this._typeaheadLength = 0; this._onChange = () => onChange(this._value, { @@ -58,15 +58,15 @@ class Prompt { this._typeaheadLength = length; } - setSelected(selected: string) { - this._selected = selected; + setTypheadheadSelection(selected: string) { + this._typeaheadSelection = selected; } put(key: string) { switch (key) { case KEYS.ENTER: this._entering = false; - this._onSuccess(this._selected || this._value); + this._onSuccess(this._typeaheadSelection || this._value); this.abort(); break; case KEYS.ESCAPE: @@ -92,7 +92,7 @@ class Prompt { ? this._value.slice(0, -1) : this._value + char; this._typeaheadOffset = -1; - this._selected = null; + this._typeaheadSelection = null; this._onChange(); break; } From ffc8b94af58188795a5e5eefb583000279b10534 Mon Sep 17 00:00:00 2001 From: Rogelio Guzman Date: Thu, 27 Apr 2017 12:59:21 -0700 Subject: [PATCH 06/11] Fix prompt test --- packages/jest-cli/src/lib/__tests__/Prompt-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-cli/src/lib/__tests__/Prompt-test.js b/packages/jest-cli/src/lib/__tests__/Prompt-test.js index c10cdf18afed..9598621b8270 100644 --- a/packages/jest-cli/src/lib/__tests__/Prompt-test.js +++ b/packages/jest-cli/src/lib/__tests__/Prompt-test.js @@ -18,7 +18,7 @@ KEYS = Object.assign({}, KEYS, { }); it('calls handler on change value', () => { - const options = {offset: -1}; + const options = {max: 10, offset: -1}; const prompt = new Prompt(); const onChange = jest.fn(); From 4ff89183f2a4caf29c453da95f0112e39f3997b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 4 May 2017 20:22:02 +0200 Subject: [PATCH 07/11] Refactor pattern prompts --- packages/jest-cli/src/PatternPrompt.js | 61 ++++++ .../jest-cli/src/TestNamePatternPrompt.js | 109 +++------- .../jest-cli/src/TestPathPatternPrompt.js | 142 +++++-------- .../watch-filename-pattern-mode-test.js.snap | 192 +++++++++--------- .../watch-test-name-pattern-mode-test.js.snap | 33 +-- .../watch-filename-pattern-mode-test.js | 11 +- .../watch-test-name-pattern-mode-test.js | 11 +- packages/jest-cli/src/lib/Prompt.js | 18 +- .../src/lib/__tests__/scrollList-test.js | 16 +- .../jest-cli/src/lib/patternModeHelpers.js | 104 ++++++++++ packages/jest-cli/src/lib/scrollList.js | 7 +- 11 files changed, 390 insertions(+), 314 deletions(-) create mode 100644 packages/jest-cli/src/PatternPrompt.js create mode 100644 packages/jest-cli/src/lib/patternModeHelpers.js diff --git a/packages/jest-cli/src/PatternPrompt.js b/packages/jest-cli/src/PatternPrompt.js new file mode 100644 index 000000000000..cef517aef5d2 --- /dev/null +++ b/packages/jest-cli/src/PatternPrompt.js @@ -0,0 +1,61 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ + +'use strict'; + +import type {ScrollOptions} from './lib/scrollList'; + +const chalk = require('chalk'); +const ansiEscapes = require('ansi-escapes'); +const Prompt = require('./lib/Prompt'); + +const usage = (entity: string) => + `\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; + +module.exports = 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); + } + + _onChange(pattern: string, options: ScrollOptions) { + this._pipe.write(ansiEscapes.eraseLine); + this._pipe.write(ansiEscapes.cursorLeft); + } +}; diff --git a/packages/jest-cli/src/TestNamePatternPrompt.js b/packages/jest-cli/src/TestNamePatternPrompt.js index c7c2a66b3ff4..b3ce7973ad81 100644 --- a/packages/jest-cli/src/TestNamePatternPrompt.js +++ b/packages/jest-cli/src/TestNamePatternPrompt.js @@ -13,114 +13,65 @@ import type {TestResult} from 'types/TestResult'; import type {ScrollOptions} from './lib/scrollList'; -const ansiEscapes = require('ansi-escapes'); -const chalk = require('chalk'); const scroll = require('./lib/scrollList'); const {getTerminalWidth} = require('./lib/terminalUtils'); -const stringLength = require('string-length'); const Prompt = require('./lib/Prompt'); const formatTestNameByPattern = require('./lib/formatTestNameByPattern'); - -const pluralizeTest = (total: number) => (total === 1 ? 'test' : 'tests'); - -const usage = () => - `\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 tests.')}\n` + - `\n`; - -const usageRows = usage().split('\n').length; - -module.exports = class TestNamePatternPrompt { +const { + formatTypeaheadSelection, + printMore, + printPatternCaret, + printPatternMatches, + printRestoredPatternCaret, + printStartTyping, + printTypeaheadItem, +} = require('./lib/patternModeHelpers'); +const PatternPrompt = require('./PatternPrompt'); + +module.exports = class TestNamePatternPrompt extends PatternPrompt { _cachedTestResults: Array; - _pipe: stream$Writable | tty$WriteStream; - _prompt: Prompt; - _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._pipe.write(ansiEscapes.cursorShow); - - this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel); + super(pipe, prompt); + this._entityName = 'tests'; } _onChange(pattern: string, options: ScrollOptions) { - this._pipe.write(ansiEscapes.eraseLine); - this._pipe.write(ansiEscapes.cursorLeft); + super._onChange(pattern, options); this._printTypeahead(pattern, options); } _printTypeahead(pattern: string, options: ScrollOptions) { - const { max } = options; + const {max} = options; const matchedTests = this._getMatchedTests(pattern); - const total = matchedTests.length; - const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; + const pipe = this._pipe; + const prompt = this._prompt; - this._pipe.write(ansiEscapes.eraseDown); - this._pipe.write(inputText); - this._pipe.write(ansiEscapes.cursorSavePosition); + printPatternCaret(pattern, pipe); if (pattern) { - if (total) { - this._pipe.write( - `\n\n Pattern matches ${total} ${pluralizeTest(total)}`, - ); - } else { - this._pipe.write(`\n\n Pattern matches no tests`); - } - - this._pipe.write(' from cached test suites.'); + printPatternMatches(total, 'test', pipe, ` from cached test suites`); const width = getTerminalWidth(); - const { start, end, index } = scroll(total, options); + const {start, end, index} = scroll(total, options); - this._prompt.setTypeaheadLength(total); + prompt.setTypeaheadLength(total); - matchedTests.slice(start, end) - .map(name => formatTestNameByPattern(name, pattern, width - 4)) - .map((item, i) => { - if (i === index) { - this._prompt.setTypheadheadSelection(chalk.stripColor(item)); - return chalk.black.bgYellow(chalk.stripColor(item)); - } - return item; - }) - .forEach(output => this._pipe.write(`\n ${chalk.dim('\u203A')} ${output}`)); + matchedTests + .slice(start, end) + .map(name => formatTestNameByPattern(name, pattern, width - 4)) + .map((item, i) => formatTypeaheadSelection(item, i, index, prompt)) + .forEach(item => printTypeaheadItem(item, pipe)); if (total > max) { - const more = total - max; - this._pipe.write( - // eslint-disable-next-line max-len - `\n ${chalk.dim(`\u203A and ${more} more ${pluralizeTest(more)}`)}`, - ); + printMore('test', pipe, total - max); } } else { - this._pipe.write( - // eslint-disable-next-line max-len - `\n\n ${chalk.italic.yellow('Start typing to filter by a test name regex pattern.')}`, - ); + printStartTyping('test name', pipe); } - this._pipe.write( - ansiEscapes.cursorTo(stringLength(inputText), this._currentUsageRows - 1), - ); - this._pipe.write(ansiEscapes.cursorRestorePosition); + printRestoredPatternCaret(pattern, this._currentUsageRows, pipe); } _getMatchedTests(pattern: string) { diff --git a/packages/jest-cli/src/TestPathPatternPrompt.js b/packages/jest-cli/src/TestPathPatternPrompt.js index 27947d1d1a25..12026a57296c 100644 --- a/packages/jest-cli/src/TestPathPatternPrompt.js +++ b/packages/jest-cli/src/TestPathPatternPrompt.js @@ -15,7 +15,6 @@ import type {Test} from 'types/TestRunner'; import type {ScrollOptions} from './lib/scrollList'; import type SearchSource from './SearchSource'; -const ansiEscapes = require('ansi-escapes'); const chalk = require('chalk'); const scroll = require('./lib/scrollList'); const {getTerminalWidth} = require('./lib/terminalUtils'); @@ -23,96 +22,56 @@ const highlight = require('./lib/highlight'); const stringLength = require('string-length'); const {trimAndFormatPath} = require('./reporters/utils'); const Prompt = require('./lib/Prompt'); +const { + formatTypeaheadSelection, + printMore, + printPatternCaret, + printPatternMatches, + printRestoredPatternCaret, + printStartTyping, + printTypeaheadItem, +} = require('./lib/patternModeHelpers'); +const PatternPrompt = require('./PatternPrompt'); type SearchSources = Array<{| context: Context, searchSource: SearchSource, |}>; -const pluralizeFile = (total: number) => (total === 1 ? 'file' : 'files'); - -const usage = () => - `\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 filenames.')}\n` + - `\n`; - -const usageRows = usage().split('\n').length; - -module.exports = class TestPathPatternPrompt { - _pipe: stream$Writable | tty$WriteStream; - _prompt: Prompt; +module.exports = class TestPathPatternPrompt extends PatternPrompt { _searchSources: SearchSources; - _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._pipe.write(ansiEscapes.cursorShow); - - this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel); + super(pipe, prompt); + this._entityName = 'filenames'; } _onChange(pattern: string, options: ScrollOptions) { - let regex; - - try { - regex = new RegExp(pattern, 'i'); - } catch (e) {} - - let tests = []; - if (regex) { - this._searchSources.forEach(({searchSource, context}) => { - tests = tests.concat(searchSource.findMatchingTests(pattern).tests); - }); - } - - this._pipe.write(ansiEscapes.eraseLine); - this._pipe.write(ansiEscapes.cursorLeft); - this._printTypeahead(pattern, tests, options); + super._onChange(pattern, options); + this._printTypeahead(pattern, options); } - _printTypeahead(pattern: string, allResults: Array, options: ScrollOptions) { - const { max } = options; - const total = allResults.length; - const results = allResults.slice(0, max); - const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`; + _printTypeahead(pattern: string, options: ScrollOptions) { + const {max} = options; + const matchedTests = this._getMatchedTests(pattern); + const total = matchedTests.length; + const pipe = this._pipe; + const prompt = this._prompt; - this._pipe.write(ansiEscapes.eraseDown); - this._pipe.write(inputText); - this._pipe.write(ansiEscapes.cursorSavePosition); + printPatternCaret(pattern, pipe); if (pattern) { - if (total) { - this._pipe.write( - `\n\n Pattern matches ${total} ${pluralizeFile(total)}.`, - ); - } else { - this._pipe.write(`\n\n Pattern matches no files.`); - } + printPatternMatches(total, 'file', pipe); - const width = getTerminalWidth(); const prefix = ` ${chalk.dim('\u203A')} `; const padding = stringLength(prefix) + 2; + const width = getTerminalWidth(); + const {start, end, index} = scroll(total, options); - const { start, end, index } = scroll(total, options); + prompt.setTypeaheadLength(total); - this._prompt.setTypeaheadLength(total); - allResults.slice(start, end) + matchedTests + .slice(start, end) .map(({path, context}) => { const filePath = trimAndFormatPath( padding, @@ -122,35 +81,34 @@ module.exports = class TestPathPatternPrompt { ); return highlight(path, filePath, pattern, context.config.rootDir); }) - .map((item, i) => { - if (i === index) { - this._prompt.setTypheadheadSelection(chalk.stripColor(item)); - return chalk.black.bgYellow(chalk.stripColor(item)); - } - return item; - }) - .forEach(filePath => - this._pipe.write(`\n ${chalk.dim('\u203A')} ${filePath}`), - ); + .map((item, i) => formatTypeaheadSelection(item, i, index, prompt)) + .forEach(item => printTypeaheadItem(item, pipe)); if (total > max) { - const more = total - max; - this._pipe.write( - // eslint-disable-next-line max-len - `\n ${chalk.dim(`\u203A and ${more} more ${pluralizeFile(more)}`)}`, - ); + printMore('file', pipe, total - max); } } else { - this._pipe.write( - // eslint-disable-next-line max-len - `\n\n ${chalk.italic.yellow('Start typing to filter by a filename regex pattern.')}`, - ); + printStartTyping('filename', pipe); + } + + printRestoredPatternCaret(pattern, this._currentUsageRows, pipe); + } + + _getMatchedTests(pattern: string): Array { + let regex; + + try { + regex = new RegExp(pattern, 'i'); + } catch (e) {} + + let tests = []; + if (regex) { + this._searchSources.forEach(({searchSource, context}) => { + tests = tests.concat(searchSource.findMatchingTests(pattern).tests); + }); } - this._pipe.write( - ansiEscapes.cursorTo(stringLength(inputText), this._currentUsageRows - 1), - ); - this._pipe.write(ansiEscapes.cursorRestorePosition); + return tests; } updateSearchSources(searchSources: SearchSources) { diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap index dd5740b852f0..bf98d9fe0e04 100644 --- a/packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap +++ b/packages/jest-cli/src/__tests__/__snapshots__/watch-filename-pattern-mode-test.js.snap @@ -8,29 +8,29 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 1`] = ` [MOCK - cursorSavePosition] - Pattern matches 11 files. + Pattern matches 11 files - › path/to/file1-test.js + › path/to/file1-test.js - › path/to/file2-test.js + › path/to/file2-test.js - › path/to/file3-test.js + › path/to/file3-test.js - › path/to/file4-test.js + › path/to/file4-test.js - › path/to/file5-test.js + › path/to/file5-test.js - › path/to/file6-test.js + › path/to/file6-test.js - › path/to/file7-test.js + › path/to/file7-test.js - › path/to/file8-test.js + › path/to/file8-test.js - › path/to/file9-test.js + › path/to/file9-test.js - › path/to/file10-test.js + › path/to/file10-test.js - › and 1 more file + ...and 1 more file [MOCK - cursorTo(12, 5)] [MOCK - cursorRestorePosition]" `; @@ -43,29 +43,29 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 2`] = ` [MOCK - cursorSavePosition] - Pattern matches 11 files. + Pattern matches 11 files - › path/to/file1-test.js + › path/to/file1-test.js - › path/to/file2-test.js + › path/to/file2-test.js - › path/to/file3-test.js + › path/to/file3-test.js - › path/to/file4-test.js + › path/to/file4-test.js - › path/to/file5-test.js + › path/to/file5-test.js - › path/to/file6-test.js + › path/to/file6-test.js - › path/to/file7-test.js + › path/to/file7-test.js - › path/to/file8-test.js + › path/to/file8-test.js - › path/to/file9-test.js + › path/to/file9-test.js - › path/to/file10-test.js + › path/to/file10-test.js - › and 1 more file + ...and 1 more file [MOCK - cursorTo(13, 5)] [MOCK - cursorRestorePosition]" `; @@ -78,29 +78,29 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 3`] = ` [MOCK - cursorSavePosition] - Pattern matches 11 files. + Pattern matches 11 files - › path/to/file1-test.js + › path/to/file1-test.js - › path/to/file2-test.js + › path/to/file2-test.js - › path/to/file3-test.js + › path/to/file3-test.js - › path/to/file4-test.js + › path/to/file4-test.js - › path/to/file5-test.js + › path/to/file5-test.js - › path/to/file6-test.js + › path/to/file6-test.js - › path/to/file7-test.js + › path/to/file7-test.js - › path/to/file8-test.js + › path/to/file8-test.js - › path/to/file9-test.js + › path/to/file9-test.js - › path/to/file10-test.js + › path/to/file10-test.js - › and 1 more file + ...and 1 more file [MOCK - cursorTo(14, 5)] [MOCK - cursorRestorePosition]" `; @@ -113,13 +113,13 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 4`] = ` [MOCK - cursorSavePosition] - Pattern matches 3 files. + Pattern matches 3 files - › path/to/file1-test.js + › path/to/file1-test.js - › path/to/file10-test.js + › path/to/file10-test.js - › path/to/file11-test.js + › path/to/file11-test.js [MOCK - cursorTo(15, 5)] [MOCK - cursorRestorePosition]" `; @@ -132,9 +132,9 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 5`] = ` [MOCK - cursorSavePosition] - Pattern matches 1 file. + Pattern matches 1 file - › path/to/file10-test.js + › path/to/file10-test.js [MOCK - cursorTo(16, 5)] [MOCK - cursorRestorePosition]" `; @@ -147,13 +147,13 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 6`] = ` [MOCK - cursorSavePosition] - Pattern matches 3 files. + Pattern matches 3 files - › path/to/file1-test.js + › path/to/file1-test.js - › path/to/file10-test.js + › path/to/file10-test.js - › path/to/file11-test.js + › path/to/file11-test.js [MOCK - cursorTo(15, 5)] [MOCK - cursorRestorePosition]" `; @@ -166,29 +166,29 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 7`] = ` [MOCK - cursorSavePosition] - Pattern matches 11 files. + Pattern matches 11 files - › path/to/file1-test.js + › path/to/file1-test.js - › path/to/file2-test.js + › path/to/file2-test.js - › path/to/file3-test.js + › path/to/file3-test.js - › path/to/file4-test.js + › path/to/file4-test.js - › path/to/file5-test.js + › path/to/file5-test.js - › path/to/file6-test.js + › path/to/file6-test.js - › path/to/file7-test.js + › path/to/file7-test.js - › path/to/file8-test.js + › path/to/file8-test.js - › path/to/file9-test.js + › path/to/file9-test.js - › path/to/file10-test.js + › path/to/file10-test.js - › and 1 more file + ...and 1 more file [MOCK - cursorTo(14, 5)] [MOCK - cursorRestorePosition]" `; @@ -201,9 +201,9 @@ exports[`Watch mode flows Pressing "P" enters pattern mode 8`] = ` [MOCK - cursorSavePosition] - Pattern matches 1 file. + Pattern matches 1 file - › path/to/file3-test.js + › path/to/file3-test.js [MOCK - cursorTo(15, 5)] [MOCK - cursorRestorePosition]" `; @@ -238,29 +238,29 @@ exports[`Watch mode flows Results in pattern mode get truncated appropriately 1` [MOCK - cursorSavePosition] - Pattern matches 11 files. + Pattern matches 11 files - › path/to/file1-test.js + › path/to/file1-test.js - › path/to/file2-test.js + › path/to/file2-test.js - › path/to/file3-test.js + › path/to/file3-test.js - › path/to/file4-test.js + › path/to/file4-test.js - › path/to/file5-test.js + › path/to/file5-test.js - › path/to/file6-test.js + › path/to/file6-test.js - › path/to/file7-test.js + › path/to/file7-test.js - › path/to/file8-test.js + › path/to/file8-test.js - › path/to/file9-test.js + › path/to/file9-test.js - › path/to/file10-test.js + › path/to/file10-test.js - › and 1 more file + ...and 1 more file [MOCK - cursorTo(12, 5)] [MOCK - cursorRestorePosition]" `; @@ -273,29 +273,29 @@ exports[`Watch mode flows Results in pattern mode get truncated appropriately 2` [MOCK - cursorSavePosition] - Pattern matches 11 files. + Pattern matches 11 files - › ...to/file1-test.js + › ...to/file1-test.js - › ...to/file2-test.js + › ...to/file2-test.js - › ...to/file3-test.js + › ...to/file3-test.js - › ...to/file4-test.js + › ...to/file4-test.js - › ...to/file5-test.js + › ...to/file5-test.js - › ...to/file6-test.js + › ...to/file6-test.js - › ...to/file7-test.js + › ...to/file7-test.js - › ...to/file8-test.js + › ...to/file8-test.js - › ...to/file9-test.js + › ...to/file9-test.js - › ...o/file10-test.js + › ...o/file10-test.js - › and 1 more file + ...and 1 more file [MOCK - cursorTo(12, 5)] [MOCK - cursorRestorePosition]" `; @@ -308,29 +308,29 @@ exports[`Watch mode flows Results in pattern mode get truncated appropriately 3` [MOCK - cursorSavePosition] - Pattern matches 11 files. + Pattern matches 11 files - › ...st.js + › ...st.js - › ...st.js + › ...st.js - › ...st.js + › ...st.js - › ...st.js + › ...st.js - › ...st.js + › ...st.js - › ...st.js + › ...st.js - › ...st.js + › ...st.js - › ...st.js + › ...st.js - › ...st.js + › ...st.js - › ...t.js + › ...t.js - › and 1 more file + ...and 1 more file [MOCK - cursorTo(12, 5)] [MOCK - cursorRestorePosition]" `; diff --git a/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap b/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap index c7c90b75c991..019304fe9f88 100644 --- a/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap +++ b/packages/jest-cli/src/__tests__/__snapshots__/watch-test-name-pattern-mode-test.js.snap @@ -8,8 +8,7 @@ exports[`Watch mode flows Pressing "T" enters pattern mode 1`] = ` [MOCK - cursorSavePosition] - Pattern matches 9 tests - from cached test suites. + Pattern matches 9 tests from cached test suites › should return the correct index when @@ -40,8 +39,7 @@ exports[`Watch mode flows Pressing "T" enters pattern mode 2`] = ` [MOCK - cursorSavePosition] - Pattern matches 8 tests - from cached test suites. + Pattern matches 8 tests from cached test suites › should return the correct index when @@ -70,8 +68,7 @@ exports[`Watch mode flows Pressing "T" enters pattern mode 3`] = ` [MOCK - cursorSavePosition] - Pattern matches 4 tests - from cached test suites. + Pattern matches 4 tests from cached test suites › might get confusing @@ -92,8 +89,7 @@ exports[`Watch mode flows Pressing "T" enters pattern mode 4`] = ` [MOCK - cursorSavePosition] - Pattern matches no tests - from cached test suites. + Pattern matches no tests from cached test suites [MOCK - cursorTo(15, 5)] [MOCK - cursorRestorePosition]" `; @@ -106,8 +102,7 @@ exports[`Watch mode flows Pressing "T" enters pattern mode 5`] = ` [MOCK - cursorSavePosition] - Pattern matches no tests - from cached test suites. + Pattern matches no tests from cached test suites [MOCK - cursorTo(16, 5)] [MOCK - cursorRestorePosition]" `; @@ -120,8 +115,7 @@ exports[`Watch mode flows Pressing "T" enters pattern mode 6`] = ` [MOCK - cursorSavePosition] - Pattern matches no tests - from cached test suites. + Pattern matches no tests from cached test suites [MOCK - cursorTo(17, 5)] [MOCK - cursorRestorePosition]" `; @@ -134,8 +128,7 @@ exports[`Watch mode flows Pressing "T" enters pattern mode 7`] = ` [MOCK - cursorSavePosition] - Pattern matches no tests - from cached test suites. + Pattern matches no tests from cached test suites [MOCK - cursorTo(16, 5)] [MOCK - cursorRestorePosition]" `; @@ -148,8 +141,7 @@ exports[`Watch mode flows Pressing "T" enters pattern mode 8`] = ` [MOCK - cursorSavePosition] - Pattern matches no tests - from cached test suites. + Pattern matches no tests from cached test suites [MOCK - cursorTo(15, 5)] [MOCK - cursorRestorePosition]" `; @@ -162,8 +154,7 @@ exports[`Watch mode flows Pressing "T" enters pattern mode 9`] = ` [MOCK - cursorSavePosition] - Pattern matches 4 tests - from cached test suites. + Pattern matches 4 tests from cached test suites › might get confusing @@ -184,8 +175,7 @@ exports[`Watch mode flows Results in pattern mode get truncated appropriately 1` [MOCK - cursorSavePosition] - Pattern matches 9 tests - from cached test suites. + Pattern matches 9 tests from cached test suites › should return the correct index when @@ -216,8 +206,7 @@ exports[`Watch mode flows Results in pattern mode get truncated appropriately 2` [MOCK - cursorSavePosition] - Pattern matches 9 tests - from cached test suites. + Pattern matches 9 tests from cached test suites › should return the corre... diff --git a/packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js b/packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js index 03f68a501409..2d3429fcb842 100644 --- a/packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js +++ b/packages/jest-cli/src/__tests__/watch-filename-pattern-mode-test.js @@ -153,9 +153,14 @@ describe('Watch mode flows', () => { // Write a pattern ['p', '.', '*'] - .map(toHex) - .concat([KEYS.ARROW_DOWN, KEYS.ARROW_DOWN, KEYS.ARROW_DOWN, KEYS.ARROW_UP]) - .forEach(key => stdin.emit(key)); + .map(toHex) + .concat([ + KEYS.ARROW_DOWN, + KEYS.ARROW_DOWN, + KEYS.ARROW_DOWN, + KEYS.ARROW_UP, + ]) + .forEach(key => stdin.emit(key)); stdin.emit(KEYS.ENTER); diff --git a/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js b/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js index de9abb7ce6ad..e93e28548bcc 100644 --- a/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js +++ b/packages/jest-cli/src/__tests__/watch-test-name-pattern-mode-test.js @@ -169,9 +169,14 @@ describe('Watch mode flows', () => { // Write a pattern ['c', 'o', 'n'] - .map(toHex) - .concat([KEYS.ARROW_DOWN, KEYS.ARROW_DOWN, KEYS.ARROW_DOWN, KEYS.ARROW_UP]) - .forEach(key => stdin.emit(key)); + .map(toHex) + .concat([ + KEYS.ARROW_DOWN, + KEYS.ARROW_DOWN, + KEYS.ARROW_DOWN, + KEYS.ARROW_UP, + ]) + .forEach(key => stdin.emit(key)); stdin.emit(KEYS.ENTER); diff --git a/packages/jest-cli/src/lib/Prompt.js b/packages/jest-cli/src/lib/Prompt.js index 7510402d23f8..1959f3682b83 100644 --- a/packages/jest-cli/src/lib/Prompt.js +++ b/packages/jest-cli/src/lib/Prompt.js @@ -17,12 +17,12 @@ const {KEYS} = require('../constants'); class Prompt { _entering: boolean; _value: string; - _onChange: (Function); + _onChange: Function; _onSuccess: Function; _onCancel: Function; _typeaheadOffset: number; _typeaheadLength: number; - _typeaheadSelection: string|null; + _typeaheadSelection: string | null; constructor() { (this: any)._onResize = this._onResize.bind(this); @@ -44,10 +44,11 @@ class Prompt { this._typeaheadSelection = null; this._typeaheadOffset = -1; this._typeaheadLength = 0; - this._onChange = () => onChange(this._value, { - max: 10, - offset: this._typeaheadOffset, - }); + this._onChange = () => + onChange(this._value, { + max: 10, + offset: this._typeaheadOffset, + }); this._onChange(); @@ -75,7 +76,10 @@ class Prompt { this.abort(); break; case KEYS.ARROW_DOWN: - this._typeaheadOffset = Math.min(this._typeaheadOffset + 1, this._typeaheadLength - 1); + this._typeaheadOffset = Math.min( + this._typeaheadOffset + 1, + this._typeaheadLength - 1, + ); this._onChange(); break; case KEYS.ARROW_UP: diff --git a/packages/jest-cli/src/lib/__tests__/scrollList-test.js b/packages/jest-cli/src/lib/__tests__/scrollList-test.js index 8a47f12b0bde..eb869ef55754 100644 --- a/packages/jest-cli/src/lib/__tests__/scrollList-test.js +++ b/packages/jest-cli/src/lib/__tests__/scrollList-test.js @@ -1,7 +1,7 @@ const scroll = require('../scrollList'); it('When offset is -1', () => { - expect(scroll(25, { offset: -1, max: 10 })).toEqual({ + expect(scroll(25, {offset: -1, max: 10})).toEqual({ end: 10, index: -1, start: 0, @@ -9,13 +9,13 @@ it('When offset is -1', () => { }); it('When offset is in the first set of items', () => { - expect(scroll(25, { offset: 4, max: 10 })).toEqual({ + expect(scroll(25, {offset: 4, max: 10})).toEqual({ end: 10, index: 4, start: 0, }); - expect(scroll(25, { offset: 6, max: 10 })).toEqual({ + expect(scroll(25, {offset: 6, max: 10})).toEqual({ end: 10, index: 6, start: 0, @@ -23,13 +23,13 @@ it('When offset is in the first set of items', () => { }); it('When offset is in the middle of the list', () => { - expect(scroll(25, { offset: 7, max: 10 })).toEqual({ + expect(scroll(25, {offset: 7, max: 10})).toEqual({ end: 11, index: 6, start: 1, }); - expect(scroll(25, { offset: 14, max: 10 })).toEqual({ + expect(scroll(25, {offset: 14, max: 10})).toEqual({ end: 18, index: 6, start: 8, @@ -37,19 +37,19 @@ it('When offset is in the middle of the list', () => { }); it('When offset is at the end of the list', () => { - expect(scroll(25, { offset: 23, max: 10 })).toEqual({ + expect(scroll(25, {offset: 23, max: 10})).toEqual({ end: 25, index: 8, start: 15, }); - expect(scroll(25, { offset: 25, max: 10 })).toEqual({ + expect(scroll(25, {offset: 25, max: 10})).toEqual({ end: 25, index: 10, start: 15, }); - expect(scroll(25, { offset: 35, max: 10 })).toEqual({ + expect(scroll(25, {offset: 35, max: 10})).toEqual({ end: 25, index: 10, start: 15, diff --git a/packages/jest-cli/src/lib/patternModeHelpers.js b/packages/jest-cli/src/lib/patternModeHelpers.js new file mode 100644 index 000000000000..ed0eb2334874 --- /dev/null +++ b/packages/jest-cli/src/lib/patternModeHelpers.js @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @flow + */ + +'use strict'; + +const chalk = require('chalk'); +const ansiEscapes = require('ansi-escapes'); +const stringLength = require('string-length'); +const Prompt = require('./Prompt'); + +const pluralize = (count: number, text: string) => + count === 1 ? text : text + 's'; + +const printPatternMatches = ( + count: number, + entity: string, + pipe: stream$Writable | tty$WriteStream, + extraText: string = '', +) => { + const pluralized = pluralize(count, entity); + const result = count + ? `\n\n Pattern matches ${count} ${pluralized}` + : `\n\n Pattern matches no ${pluralized}`; + + pipe.write(result + extraText); +}; + +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); +}; + +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); +}; + +const printStartTyping = ( + entity: string, + pipe: stream$Writable | tty$WriteStream, +) => { + pipe.write( + `\n\n ${chalk.italic.yellow(`Start typing to filter by a ${entity} regex pattern.`)}`, + ); +}; + +const printMore = ( + entity: string, + pipe: stream$Writable | tty$WriteStream, + more: number, +) => { + pipe.write( + `\n ${chalk.dim(`...and ${more} more ${pluralize(more, entity)}`)}`, + ); +}; + +const printTypeaheadItem = ( + item: string, + pipe: stream$Writable | tty$WriteStream, +) => pipe.write(`\n ${chalk.dim('\u203A')} ${item}`); + +const formatTypeaheadSelection = ( + item: string, + index: number, + activeIndex: number, + prompt: Prompt, +) => { + if (index === activeIndex) { + prompt.setTypheadheadSelection(chalk.stripColor(item)); + return chalk.black.bgYellow(chalk.stripColor(item)); + } + return item; +}; + +module.exports = { + formatTypeaheadSelection, + printMore, + printPatternCaret, + printPatternMatches, + printRestoredPatternCaret, + printStartTyping, + printTypeaheadItem, +}; diff --git a/packages/jest-cli/src/lib/scrollList.js b/packages/jest-cli/src/lib/scrollList.js index d614409f9821..a1c58b29b847 100644 --- a/packages/jest-cli/src/lib/scrollList.js +++ b/packages/jest-cli/src/lib/scrollList.js @@ -13,9 +13,9 @@ export type ScrollOptions = { offset: number, max: number, -} +}; -const scroll = (size: number, { offset, max }: ScrollOptions) => { +const scroll = (size: number, {offset, max}: ScrollOptions) => { let start = 0; let index = Math.min(offset, size); @@ -33,7 +33,6 @@ const scroll = (size: number, { offset, max }: ScrollOptions) => { index, start, }; -} - +}; module.exports = scroll; From b2531110bef6063585c550e3129a0e97e4c0675b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 4 May 2017 21:44:26 +0200 Subject: [PATCH 08/11] Stress 'cached' in TestNamePatternPrompt --- packages/jest-cli/src/TestNamePatternPrompt.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/jest-cli/src/TestNamePatternPrompt.js b/packages/jest-cli/src/TestNamePatternPrompt.js index b3ce7973ad81..58206fc2f287 100644 --- a/packages/jest-cli/src/TestNamePatternPrompt.js +++ b/packages/jest-cli/src/TestNamePatternPrompt.js @@ -34,6 +34,7 @@ module.exports = class TestNamePatternPrompt extends PatternPrompt { constructor(pipe: stream$Writable | tty$WriteStream, prompt: Prompt) { super(pipe, prompt); this._entityName = 'tests'; + this._cachedTestResults = []; } _onChange(pattern: string, options: ScrollOptions) { @@ -51,7 +52,12 @@ module.exports = class TestNamePatternPrompt extends PatternPrompt { printPatternCaret(pattern, pipe); if (pattern) { - printPatternMatches(total, 'test', pipe, ` from cached test suites`); + printPatternMatches( + total, + 'test', + pipe, + ` from ${require('chalk').yellow('cached')} test suites`, + ); const width = getTerminalWidth(); const {start, end, index} = scroll(total, options); @@ -96,7 +102,7 @@ module.exports = class TestNamePatternPrompt extends PatternPrompt { return matchedTests; } - updateCachedTestResults(testResults: Array) { - this._cachedTestResults = testResults || []; + updateCachedTestResults(testResults: Array = []) { + this._cachedTestResults = testResults; } }; From 23ea04cfd81dd4a3d2d88db69a7c02f475695547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 4 May 2017 22:27:51 +0200 Subject: [PATCH 09/11] Fix eslint --- .../src/lib/__tests__/scrollList-test.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/jest-cli/src/lib/__tests__/scrollList-test.js b/packages/jest-cli/src/lib/__tests__/scrollList-test.js index eb869ef55754..993bb24f3a29 100644 --- a/packages/jest-cli/src/lib/__tests__/scrollList-test.js +++ b/packages/jest-cli/src/lib/__tests__/scrollList-test.js @@ -1,7 +1,7 @@ const scroll = require('../scrollList'); it('When offset is -1', () => { - expect(scroll(25, {offset: -1, max: 10})).toEqual({ + expect(scroll(25, {max: 10, offset: -1})).toEqual({ end: 10, index: -1, start: 0, @@ -9,13 +9,13 @@ it('When offset is -1', () => { }); it('When offset is in the first set of items', () => { - expect(scroll(25, {offset: 4, max: 10})).toEqual({ + expect(scroll(25, {max: 10, offset: 4})).toEqual({ end: 10, index: 4, start: 0, }); - expect(scroll(25, {offset: 6, max: 10})).toEqual({ + expect(scroll(25, {max: 10, offset: 6})).toEqual({ end: 10, index: 6, start: 0, @@ -23,13 +23,13 @@ it('When offset is in the first set of items', () => { }); it('When offset is in the middle of the list', () => { - expect(scroll(25, {offset: 7, max: 10})).toEqual({ + expect(scroll(25, {max: 10, offset: 7})).toEqual({ end: 11, index: 6, start: 1, }); - expect(scroll(25, {offset: 14, max: 10})).toEqual({ + expect(scroll(25, {max: 10, offset: 14})).toEqual({ end: 18, index: 6, start: 8, @@ -37,19 +37,19 @@ it('When offset is in the middle of the list', () => { }); it('When offset is at the end of the list', () => { - expect(scroll(25, {offset: 23, max: 10})).toEqual({ + expect(scroll(25, {max: 10, offset: 23})).toEqual({ end: 25, index: 8, start: 15, }); - expect(scroll(25, {offset: 25, max: 10})).toEqual({ + expect(scroll(25, {max: 10, offset: 25})).toEqual({ end: 25, index: 10, start: 15, }); - expect(scroll(25, {offset: 35, max: 10})).toEqual({ + expect(scroll(25, {max: 10, offset: 35})).toEqual({ end: 25, index: 10, start: 15, From 06ed992ceb2e823dd8b40d3ec47e05c3e15c18a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Thu, 4 May 2017 23:48:47 +0200 Subject: [PATCH 10/11] Fix scrolling edgecase --- .../jest-cli/src/lib/__tests__/scrollList-test.js | 14 ++++++++++++++ packages/jest-cli/src/lib/scrollList.js | 4 +++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/jest-cli/src/lib/__tests__/scrollList-test.js b/packages/jest-cli/src/lib/__tests__/scrollList-test.js index 993bb24f3a29..23e4aa0a510a 100644 --- a/packages/jest-cli/src/lib/__tests__/scrollList-test.js +++ b/packages/jest-cli/src/lib/__tests__/scrollList-test.js @@ -55,3 +55,17 @@ it('When offset is at the end of the list', () => { start: 15, }); }); + +it('When offset is at the end and size is smaller than max', () => { + expect(scroll(8, {max: 10, offset: 6})).toEqual({ + end: 8, + index: 6, + start: 0, + }); + + expect(scroll(5, {max: 6, offset: 4})).toEqual({ + end: 5, + index: 4, + start: 0, + }); +}); diff --git a/packages/jest-cli/src/lib/scrollList.js b/packages/jest-cli/src/lib/scrollList.js index a1c58b29b847..7832cd3a0125 100644 --- a/packages/jest-cli/src/lib/scrollList.js +++ b/packages/jest-cli/src/lib/scrollList.js @@ -24,7 +24,9 @@ const scroll = (size: number, {offset, max}: ScrollOptions) => { if (index <= halfScreen) { start = 0; } else { - start = Math.min(index - halfScreen - 1, size - max); + if (size >= max) { + start = Math.min(index - halfScreen - 1, size - max); + } index = Math.min(index - start, size); } From 63709439c86dd7a9ca0322f60be5f4fcc5488d37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Fri, 5 May 2017 00:06:35 +0200 Subject: [PATCH 11/11] Live update number of remaining typeahead items --- packages/jest-cli/src/TestNamePatternPrompt.js | 4 ++-- packages/jest-cli/src/TestPathPatternPrompt.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/jest-cli/src/TestNamePatternPrompt.js b/packages/jest-cli/src/TestNamePatternPrompt.js index 58206fc2f287..134c8a5fc31d 100644 --- a/packages/jest-cli/src/TestNamePatternPrompt.js +++ b/packages/jest-cli/src/TestNamePatternPrompt.js @@ -70,8 +70,8 @@ module.exports = class TestNamePatternPrompt extends PatternPrompt { .map((item, i) => formatTypeaheadSelection(item, i, index, prompt)) .forEach(item => printTypeaheadItem(item, pipe)); - if (total > max) { - printMore('test', pipe, total - max); + if (total > end) { + printMore('test', pipe, total - end); } } else { printStartTyping('test name', pipe); diff --git a/packages/jest-cli/src/TestPathPatternPrompt.js b/packages/jest-cli/src/TestPathPatternPrompt.js index 12026a57296c..98c0a9736f7f 100644 --- a/packages/jest-cli/src/TestPathPatternPrompt.js +++ b/packages/jest-cli/src/TestPathPatternPrompt.js @@ -84,8 +84,8 @@ module.exports = class TestPathPatternPrompt extends PatternPrompt { .map((item, i) => formatTypeaheadSelection(item, i, index, prompt)) .forEach(item => printTypeaheadItem(item, pipe)); - if (total > max) { - printMore('file', pipe, total - max); + if (total > end) { + printMore('file', pipe, total - end); } } else { printStartTyping('filename', pipe);