Skip to content

Commit

Permalink
Refactor watch mode prompts. (jestjs#3290)
Browse files Browse the repository at this point in the history
* Refactor watch mode prompts.

* Update internal API names; remove binding in the constructor.
  • Loading branch information
cpojer authored and skovhus committed Apr 29, 2017
1 parent 70c5ea3 commit 89f8785
Show file tree
Hide file tree
Showing 3 changed files with 166 additions and 159 deletions.
159 changes: 78 additions & 81 deletions packages/jest-cli/src/TestNamePatternPrompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

'use strict';

import type {Config} from 'types/Config';
import type {TestResult} from 'types/TestResult';

const ansiEscapes = require('ansi-escapes');
const chalk = require('chalk');
Expand All @@ -30,108 +30,105 @@ const usage = () =>

const usageRows = usage().split('\n').length;

module.exports = (
config: Config,
pipe: stream$Writable | tty$WriteStream,
prompt: Prompt,
) => {
class TestNamePatternPrompt {
// $FlowFixMe
_cachedTestResults;

constructor() {
(this: any).onChange = this.onChange.bind(this);
}
module.exports = class TestNamePatternPrompt {
_cachedTestResults: Array<TestResult>;
_pipe: stream$Writable | tty$WriteStream;
_prompt: Prompt;

run(onSuccess: Function, onCancel: Function) {
pipe.write(ansiEscapes.cursorHide);
pipe.write(ansiEscapes.clearScreen);
pipe.write(usage());
pipe.write(ansiEscapes.cursorShow);
constructor(pipe: stream$Writable | tty$WriteStream, prompt: Prompt) {
this._pipe = pipe;
this._prompt = prompt;
}

prompt.enter(this.onChange, onSuccess, onCancel);
}
run(onSuccess: Function, onCancel: Function) {
this._pipe.write(ansiEscapes.cursorHide);
this._pipe.write(ansiEscapes.clearScreen);
this._pipe.write(usage());
this._pipe.write(ansiEscapes.cursorShow);

onChange(pattern: string) {
pipe.write(ansiEscapes.eraseLine);
pipe.write(ansiEscapes.cursorLeft);
this.printTypeahead(pattern, 10);
}
this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel);
}

printTypeahead(pattern: string, max: number) {
const matchedTests = this.getMatchedTests(pattern);
_onChange(pattern: string) {
this._pipe.write(ansiEscapes.eraseLine);
this._pipe.write(ansiEscapes.cursorLeft);
this._printTypeahead(pattern, 10);
}

const total = matchedTests.length;
const results = matchedTests.slice(0, max);
const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`;
_printTypeahead(pattern: string, max: number) {
const matchedTests = this._getMatchedTests(pattern);

pipe.write(ansiEscapes.eraseDown);
pipe.write(inputText);
pipe.write(ansiEscapes.cursorSavePosition);
const total = matchedTests.length;
const results = matchedTests.slice(0, max);
const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`;

if (pattern) {
if (total) {
pipe.write(`\n\n Pattern matches ${total} ${pluralizeTest(total)}`);
} else {
pipe.write(`\n\n Pattern matches no tests`);
}
this._pipe.write(ansiEscapes.eraseDown);
this._pipe.write(inputText);
this._pipe.write(ansiEscapes.cursorSavePosition);

pipe.write(' from cached test suites.');
if (pattern) {
if (total) {
this._pipe.write(
`\n\n Pattern matches ${total} ${pluralizeTest(total)}`,
);
} else {
this._pipe.write(`\n\n Pattern matches no tests`);
}

const width = getTerminalWidth();
this._pipe.write(' from cached test suites.');

results.forEach(name => {
const testName = formatTestNameByPattern(name, pattern, width - 4);
const width = getTerminalWidth();

pipe.write(`\n ${chalk.dim('\u203A')} ${testName}`);
});
results.forEach(name => {
const testName = formatTestNameByPattern(name, pattern, width - 4);

if (total > max) {
const more = total - max;
pipe.write(
// eslint-disable-next-line max-len
`\n ${chalk.dim(`\u203A and ${more} more ${pluralizeTest(more)}`)}`,
);
}
} else {
pipe.write(
this._pipe.write(`\n ${chalk.dim('\u203A')} ${testName}`);
});

if (total > max) {
const more = total - max;
this._pipe.write(
// eslint-disable-next-line max-len
`\n\n ${chalk.italic.yellow('Start typing to filter by a test name regex pattern.')}`,
`\n ${chalk.dim(`\u203A and ${more} more ${pluralizeTest(more)}`)}`,
);
}

pipe.write(ansiEscapes.cursorTo(stringLength(inputText), usageRows - 1));
pipe.write(ansiEscapes.cursorRestorePosition);
} 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.')}`,
);
}

getMatchedTests(pattern: string) {
let regex;
this._pipe.write(
ansiEscapes.cursorTo(stringLength(inputText), usageRows - 1),
);
this._pipe.write(ansiEscapes.cursorRestorePosition);
}

try {
regex = new RegExp(pattern, 'i');
} catch (e) {
return [];
}
_getMatchedTests(pattern: string) {
let regex;

const matchedTests = [];
try {
regex = new RegExp(pattern, 'i');
} catch (e) {
return [];
}

this._cachedTestResults.forEach(({testResults}) =>
testResults.forEach(({
title,
}) => {
if (regex.test(title)) {
matchedTests.push(title);
}
}));
const matchedTests = [];

return matchedTests;
}
this._cachedTestResults.forEach(({testResults}) =>
testResults.forEach(({
title,
}) => {
if (regex.test(title)) {
matchedTests.push(title);
}
}));

// $FlowFixMe
updateCachedTestResults(testResults) {
this._cachedTestResults = testResults || [];
}
return matchedTests;
}

return new TestNamePatternPrompt();
updateCachedTestResults(testResults: Array<TestResult>) {
this._cachedTestResults = testResults || [];
}
};
158 changes: 84 additions & 74 deletions packages/jest-cli/src/TestPathPatternPrompt.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,93 +33,103 @@ const usage = () =>

const usageRows = usage().split('\n').length;

module.exports = (
config: Config,
pipe: stream$Writable | tty$WriteStream,
prompt: Prompt,
) => {
class TestPathPatternPrompt {
searchSource: SearchSource;

constructor() {
(this: any).onChange = this.onChange.bind(this);
}
module.exports = class TestPathPatternPrompt {
_config: Config;
_pipe: stream$Writable | tty$WriteStream;
_prompt: Prompt;
_searchSource: SearchSource;

constructor(
config: Config,
pipe: stream$Writable | tty$WriteStream,
prompt: Prompt,
) {
this._config = config;
this._pipe = pipe;
this._prompt = prompt;
}

run(onSuccess: Function, onCancel: Function) {
pipe.write(ansiEscapes.cursorHide);
pipe.write(ansiEscapes.clearScreen);
pipe.write(usage());
pipe.write(ansiEscapes.cursorShow);
run(onSuccess: Function, onCancel: Function) {
this._pipe.write(ansiEscapes.cursorHide);
this._pipe.write(ansiEscapes.clearScreen);
this._pipe.write(usage());
this._pipe.write(ansiEscapes.cursorShow);

prompt.enter(this.onChange, onSuccess, onCancel);
}
this._prompt.enter(this._onChange.bind(this), onSuccess, onCancel);
}

onChange(pattern: string) {
let regex;
_onChange(pattern: string) {
let regex;

try {
regex = new RegExp(pattern, 'i');
} catch (e) {}
try {
regex = new RegExp(pattern, 'i');
} catch (e) {}

const paths = regex
? this.searchSource.findMatchingTests(pattern).paths
: [];
const paths = regex
? this._searchSource.findMatchingTests(pattern).paths
: [];

pipe.write(ansiEscapes.eraseLine);
pipe.write(ansiEscapes.cursorLeft);
this.printTypeahead(pattern, paths, 10);
}
this._pipe.write(ansiEscapes.eraseLine);
this._pipe.write(ansiEscapes.cursorLeft);
this._printTypeahead(pattern, paths, 10);
}

printTypeahead(pattern: string, allResults: Array<Path>, max: number) {
const total = allResults.length;
const results = allResults.slice(0, max);
const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`;

pipe.write(ansiEscapes.eraseDown);
pipe.write(inputText);
pipe.write(ansiEscapes.cursorSavePosition);

if (pattern) {
if (total) {
pipe.write(`\n\n Pattern matches ${total} ${pluralizeFile(total)}.`);
} else {
pipe.write(`\n\n Pattern matches no files.`);
}

const width = getTerminalWidth();
const prefix = ` ${chalk.dim('\u203A')} `;
const padding = stringLength(prefix) + 2;

results
.map(rawPath => {
const filePath = trimAndFormatPath(padding, config, rawPath, width);
return highlight(rawPath, filePath, pattern, config.rootDir);
})
.forEach(filePath =>
pipe.write(`\n ${chalk.dim('\u203A')} ${filePath}`));

if (total > max) {
const more = total - max;
pipe.write(
// eslint-disable-next-line max-len
`\n ${chalk.dim(`\u203A and ${more} more ${pluralizeFile(more)}`)}`,
);
}
_printTypeahead(pattern: string, allResults: Array<Path>, max: number) {
const total = allResults.length;
const results = allResults.slice(0, max);
const inputText = `${chalk.dim(' pattern \u203A')} ${pattern}`;

this._pipe.write(ansiEscapes.eraseDown);
this._pipe.write(inputText);
this._pipe.write(ansiEscapes.cursorSavePosition);

if (pattern) {
if (total) {
this._pipe.write(
`\n\n Pattern matches ${total} ${pluralizeFile(total)}.`,
);
} else {
pipe.write(
this._pipe.write(`\n\n Pattern matches no files.`);
}

const width = getTerminalWidth();
const prefix = ` ${chalk.dim('\u203A')} `;
const padding = stringLength(prefix) + 2;

results
.map(rawPath => {
const filePath = trimAndFormatPath(
padding,
this._config,
rawPath,
width,
);
return highlight(rawPath, filePath, pattern, this._config.rootDir);
})
.forEach(filePath =>
this._pipe.write(`\n ${chalk.dim('\u203A')} ${filePath}`));

if (total > max) {
const more = total - max;
this._pipe.write(
// eslint-disable-next-line max-len
`\n\n ${chalk.italic.yellow('Start typing to filter by a filename regex pattern.')}`,
`\n ${chalk.dim(`\u203A and ${more} more ${pluralizeFile(more)}`)}`,
);
}

pipe.write(ansiEscapes.cursorTo(stringLength(inputText), usageRows - 1));
pipe.write(ansiEscapes.cursorRestorePosition);
} else {
this._pipe.write(
// eslint-disable-next-line max-len
`\n\n ${chalk.italic.yellow('Start typing to filter by a filename regex pattern.')}`,
);
}

updateSearchSource(context: Context) {
this.searchSource = new SearchSource(context, config);
}
this._pipe.write(
ansiEscapes.cursorTo(stringLength(inputText), usageRows - 1),
);
this._pipe.write(ansiEscapes.cursorRestorePosition);
}

return new TestPathPatternPrompt();
updateSearchSource(context: Context) {
this._searchSource = new SearchSource(context, this._config);
}
};
Loading

0 comments on commit 89f8785

Please sign in to comment.