jest-watch-typeahead 1.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jest-watch-typeahead",
3
- "version": "1.0.0",
3
+ "version": "2.1.0",
4
4
  "main": "build/index.js",
5
5
  "exports": {
6
6
  ".": "./build/index.js",
@@ -32,10 +32,10 @@
32
32
  "typecheck": "yarn tsc -p ."
33
33
  },
34
34
  "dependencies": {
35
- "ansi-escapes": "^4.3.1",
35
+ "ansi-escapes": "^5.0.0",
36
36
  "chalk": "^4.0.0",
37
- "jest-regex-util": "^27.0.0",
38
- "jest-watcher": "^27.0.0",
37
+ "jest-regex-util": "^28.0.0",
38
+ "jest-watcher": "^28.0.0",
39
39
  "slash": "^4.0.0",
40
40
  "string-length": "^5.0.1",
41
41
  "strip-ansi": "^7.0.1"
@@ -45,32 +45,33 @@
45
45
  "@babel/core": "^7.9.6",
46
46
  "@babel/preset-env": "^7.9.6",
47
47
  "@babel/preset-typescript": "^7.10.4",
48
- "@jest/globals": "^27.2.3",
49
- "@jest/types": "^27.0.0",
50
- "@semantic-release/changelog": "^5.0.1",
51
- "@semantic-release/git": "^9.0.0",
52
- "@types/jest": "^27.0.0",
53
- "@types/node": "^14.6.4",
54
- "@typescript-eslint/eslint-plugin": "^4.0.1",
55
- "@typescript-eslint/parser": "^4.0.1",
56
- "babel-jest": "^27.0.0",
48
+ "@jest/globals": "^28.0.0",
49
+ "@jest/types": "^28.0.0",
50
+ "@semantic-release/changelog": "^6.0.1",
51
+ "@semantic-release/git": "^10.0.1",
52
+ "@types/jest": "^28.0.0",
53
+ "@types/node": "^16.0.0",
54
+ "@typescript-eslint/eslint-plugin": "^5.0.0",
55
+ "@typescript-eslint/parser": "^5.0.0",
56
+ "babel-jest": "^28.0.0",
57
57
  "babel-plugin-add-import-extension": "^1.6.0",
58
58
  "cross-env": "^7.0.3",
59
- "eslint": "^7.8.1",
60
- "eslint-config-airbnb-base": "^14.1.0",
59
+ "eslint": "^8.0.0",
60
+ "eslint-config-airbnb-base": "^15.0.0",
61
61
  "eslint-config-prettier": "^8.0.0",
62
62
  "eslint-plugin-import": "^2.20.2",
63
- "eslint-plugin-jest": "^24.0.0",
63
+ "eslint-plugin-jest": "^26.0.0",
64
64
  "eslint-plugin-prettier": "^4.0.0",
65
- "jest": "^27.0.0",
65
+ "jest": "^28.0.0",
66
+ "jest-serializer-ansi-escapes": "^2.0.1",
66
67
  "prettier": "^2.1.1",
67
68
  "rimraf": "^3.0.2",
68
- "semantic-release": "^17.4.3",
69
+ "semantic-release": "^19.0.3",
69
70
  "semver": "^7.3.5",
70
71
  "typescript": "^4.0.2"
71
72
  },
72
73
  "peerDependencies": {
73
- "jest": "^27.0.0"
74
+ "jest": "^27.0.0 || ^28.0.0 || ^29.0.0"
74
75
  },
75
76
  "jest": {
76
77
  "extensionsToTreatAsEsm": [
@@ -81,7 +82,7 @@
81
82
  "<rootDir>/testname"
82
83
  ],
83
84
  "snapshotSerializers": [
84
- "<rootDir>/node_modules/pretty-format/build/plugins/ConvertAnsi"
85
+ "jest-serializer-ansi-escapes"
85
86
  ],
86
87
  "testPathIgnorePatterns": [
87
88
  "<rootDir>/build/.*",
@@ -93,11 +94,7 @@
93
94
  ]
94
95
  },
95
96
  "engines": {
96
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
97
- },
98
- "resolutions": {
99
- "semantic-release/@semantic-release/github/fs-extra": "^9.0.0",
100
- "semantic-release/@semantic-release/npm/fs-extra": "^9.0.0"
97
+ "node": "^14.17.0 || ^16.10.0 || >=18.0.0"
101
98
  },
102
99
  "release": {
103
100
  "branches": [
@@ -111,5 +108,6 @@
111
108
  "@semantic-release/git",
112
109
  "@semantic-release/github"
113
110
  ]
114
- }
111
+ },
112
+ "packageManager": "yarn@3.2.3"
115
113
  }
@@ -1,49 +0,0 @@
1
- import { Prompt } from 'jest-watcher';
2
- import FileNamePatternPrompt from "./prompt.js";
3
- export default class FileNamePlugin {
4
- constructor({
5
- stdin,
6
- stdout,
7
- config = {}
8
- }) {
9
- this._stdin = stdin;
10
- this._stdout = stdout;
11
- this._prompt = new Prompt();
12
- this._projects = [];
13
- this._usageInfo = {
14
- key: config.key || 'p',
15
- prompt: config.prompt || 'filter by a filename regex pattern'
16
- };
17
- }
18
-
19
- apply(jestHooks) {
20
- jestHooks.onFileChange(({
21
- projects
22
- }) => {
23
- this._projects = projects;
24
- });
25
- }
26
-
27
- onKey(key) {
28
- this._prompt.put(key);
29
- }
30
-
31
- run(globalConfig, updateConfigAndRun) {
32
- const p = new FileNamePatternPrompt(this._stdout, this._prompt);
33
- p.updateSearchSources(this._projects);
34
- return new Promise((res, rej) => {
35
- p.run(testPathPattern => {
36
- updateConfigAndRun({
37
- mode: 'watch',
38
- testPathPattern
39
- });
40
- res();
41
- }, rej);
42
- });
43
- }
44
-
45
- getUsageInfo() {
46
- return this._usageInfo;
47
- }
48
-
49
- }
@@ -1,92 +0,0 @@
1
- import chalk from 'chalk';
2
- import ansiEscapes from 'ansi-escapes';
3
- import stringLength from 'string-length';
4
- import { PatternPrompt, printPatternCaret, printRestoredPatternCaret } from 'jest-watcher';
5
- import { escapeStrForRegex } from 'jest-regex-util';
6
- import { highlight, getTerminalWidth, trimAndFormatPath, removeTrimmingDots } from "../lib/utils.js";
7
- import { formatTypeaheadSelection, printMore, printPatternMatches, printStartTyping, printTypeaheadItem } from "../lib/pattern_mode_helpers.js";
8
- import scroll from "../lib/scroll.js";
9
- export default class FileNamePatternPrompt extends PatternPrompt {
10
- constructor(pipe, prompt) {
11
- super(pipe, prompt);
12
- this._entityName = 'filenames';
13
- this._searchSources = [];
14
- }
15
-
16
- _onChange(pattern, options) {
17
- super._onChange(pattern, options);
18
-
19
- this._printTypeahead(pattern, options);
20
- }
21
-
22
- _printTypeahead(pattern, options) {
23
- const matchedTests = this._getMatchedTests(pattern);
24
-
25
- const total = matchedTests.length;
26
- const pipe = this._pipe;
27
- const prompt = this._prompt;
28
- printPatternCaret(pattern, pipe);
29
- pipe.write(ansiEscapes.cursorLeft);
30
-
31
- if (pattern) {
32
- printPatternMatches(total, 'file', pipe);
33
- const prefix = ` ${chalk.dim('\u203A')} `;
34
- const padding = stringLength(prefix) + 2;
35
- const width = getTerminalWidth(pipe);
36
- const {
37
- start,
38
- end,
39
- index
40
- } = scroll(total, options);
41
- prompt.setPromptLength(total);
42
- matchedTests.slice(start, end).map(({
43
- path,
44
- context
45
- }) => {
46
- const filePath = trimAndFormatPath(padding, context.config, path, width);
47
- return highlight(path, filePath, pattern);
48
- }).map((item, i) => formatTypeaheadSelection(item, i, index, prompt)).forEach(item => printTypeaheadItem(item, pipe));
49
-
50
- if (total > end) {
51
- printMore('file', pipe, total - end);
52
- }
53
- } else {
54
- printStartTyping('filename', pipe);
55
- }
56
-
57
- printRestoredPatternCaret(pattern, this._currentUsageRows, pipe);
58
- }
59
-
60
- _getMatchedTests(pattern) {
61
- let regex;
62
-
63
- try {
64
- regex = new RegExp(pattern, 'i');
65
- } catch (e) {
66
- return [];
67
- }
68
-
69
- return this._searchSources.reduce((tests, {
70
- testPaths,
71
- config
72
- }) => {
73
- return tests.concat(testPaths.filter(testPath => regex.test(testPath)).map(path => ({
74
- path,
75
- context: {
76
- config
77
- }
78
- })));
79
- }, []);
80
- }
81
-
82
- updateSearchSources(searchSources) {
83
- this._searchSources = searchSources;
84
- }
85
-
86
- run(onSuccess, onCancel, options) {
87
- super.run(value => {
88
- onSuccess(removeTrimmingDots(value).split('/').map(escapeStrForRegex).join('/'));
89
- }, onCancel, options);
90
- }
91
-
92
- }
package/build/index.js DELETED
@@ -1,8 +0,0 @@
1
- throw new Error(`
2
- jest-watch-typeahead includes two watch plugins: The filename plugin and the testname plugin.
3
- Please configure Jest as follows:
4
- "watchPlugins": [
5
- "jest-watch-typeahead/filename",
6
- "jest-watch-typeahead/testname"
7
- ]
8
- `);
@@ -1,27 +0,0 @@
1
- import chalk from 'chalk';
2
- import stripAnsi from 'strip-ansi';
3
-
4
- const pluralize = (count, text) => count === 1 ? text : `${text}s`;
5
-
6
- export const printPatternMatches = (count, entity, pipe, extraText = '') => {
7
- const pluralized = pluralize(count, entity);
8
- const result = count ? `\n\n Pattern matches ${count} ${pluralized}` : `\n\n Pattern matches no ${pluralized}`;
9
- pipe.write(result + extraText);
10
- };
11
- export const printStartTyping = (entity, pipe) => {
12
- pipe.write(`\n\n ${chalk.italic.yellow(`Start typing to filter by a ${entity} regex pattern.`)}`);
13
- };
14
- export const printMore = (entity, pipe, more) => {
15
- pipe.write(`\n ${chalk.dim(`...and ${more} more ${pluralize(more, entity)}`)}`);
16
- };
17
- export const printTypeaheadItem = (item, pipe) => {
18
- pipe.write(`\n ${chalk.dim('\u203A')} ${item}`);
19
- };
20
- export const formatTypeaheadSelection = (item, index, activeIndex, prompt) => {
21
- if (index === activeIndex) {
22
- prompt.setPromptSelection(stripAnsi(item));
23
- return chalk.black.bgYellow(stripAnsi(item));
24
- }
25
-
26
- return item;
27
- };
@@ -1,26 +0,0 @@
1
- const scroll = (size, {
2
- offset,
3
- max
4
- }) => {
5
- let start = 0;
6
- let index = Math.min(offset, size);
7
- const halfScreen = max / 2;
8
-
9
- if (index <= halfScreen) {
10
- start = 0;
11
- } else {
12
- if (size >= max) {
13
- start = Math.min(index - halfScreen - 1, size - max);
14
- }
15
-
16
- index = Math.min(index - start, size);
17
- }
18
-
19
- return {
20
- end: Math.min(size, start + max),
21
- index,
22
- start
23
- };
24
- };
25
-
26
- export default scroll;
@@ -1,126 +0,0 @@
1
- import path from 'path';
2
- import chalk from 'chalk';
3
- import slash from 'slash';
4
- import stripAnsi from 'strip-ansi';
5
- const TRIMMING_DOTS = '...';
6
- const ENTER = '⏎';
7
-
8
- const relativePath = (config, testPath) => {
9
- const relativeTestPath = path.relative(config.cwd || config.rootDir, testPath);
10
- const dirname = path.dirname(relativeTestPath);
11
- const basename = path.basename(relativeTestPath);
12
- return {
13
- basename,
14
- dirname
15
- };
16
- };
17
-
18
- const colorize = (str, start, end) => chalk.dim(str.slice(0, start)) + chalk.reset(str.slice(start, end)) + chalk.dim(str.slice(end));
19
-
20
- export const trimAndFormatPath = (pad, config, testPath, columns) => {
21
- const maxLength = columns - pad;
22
- const relative = relativePath(config, testPath);
23
- const {
24
- basename
25
- } = relative;
26
- let {
27
- dirname
28
- } = relative; // length is ok
29
-
30
- if ((dirname + path.sep + basename).length <= maxLength) {
31
- return slash(chalk.dim(dirname + path.sep) + chalk.bold(basename));
32
- } // we can fit trimmed dirname and full basename
33
-
34
-
35
- const basenameLength = basename.length;
36
-
37
- if (basenameLength + 4 < maxLength) {
38
- const dirnameLength = maxLength - 4 - basenameLength;
39
- dirname = `${TRIMMING_DOTS}${dirname.slice(dirname.length - dirnameLength, dirname.length)}`;
40
- return slash(chalk.dim(dirname + path.sep) + chalk.bold(basename));
41
- }
42
-
43
- if (basenameLength + 4 === maxLength) {
44
- return slash(chalk.dim(`${TRIMMING_DOTS}${path.sep}`) + chalk.bold(basename));
45
- } // can't fit dirname, but can fit trimmed basename
46
-
47
-
48
- return slash(chalk.bold(`${TRIMMING_DOTS}${basename.slice(-maxLength + 3)}`));
49
- };
50
- export const getTerminalWidth = (pipe = process.stdout) => pipe.columns;
51
- export const highlight = (rawPath, filePath, pattern) => {
52
- const relativePathHead = './';
53
- let regexp;
54
-
55
- try {
56
- regexp = new RegExp(pattern, 'i');
57
- } catch (e) {
58
- return chalk.dim(filePath);
59
- }
60
-
61
- const strippedRawPath = stripAnsi(rawPath);
62
- const strippedFilePath = stripAnsi(filePath);
63
- const match = strippedRawPath.match(regexp);
64
-
65
- if (!match || match.index == null) {
66
- return chalk.dim(strippedFilePath);
67
- }
68
-
69
- const offset = strippedRawPath.length - strippedFilePath.length;
70
- let trimLength;
71
-
72
- if (strippedFilePath.startsWith(TRIMMING_DOTS)) {
73
- trimLength = TRIMMING_DOTS.length;
74
- } else if (strippedFilePath.startsWith(relativePathHead)) {
75
- trimLength = relativePathHead.length;
76
- } else {
77
- trimLength = 0;
78
- }
79
-
80
- const start = match.index - offset;
81
- const end = start + match[0].length;
82
- return colorize(strippedFilePath, Math.max(start, 0), Math.max(end, trimLength));
83
- };
84
- export const formatTestNameByPattern = (testName, pattern, width) => {
85
- const inlineTestName = testName.replace(/(\r\n|\n|\r)/gm, ENTER);
86
- let regexp;
87
-
88
- try {
89
- regexp = new RegExp(pattern, 'i');
90
- } catch (e) {
91
- return chalk.dim(inlineTestName);
92
- }
93
-
94
- const match = inlineTestName.match(regexp);
95
-
96
- if (!match || match.index == null) {
97
- return chalk.dim(inlineTestName);
98
- }
99
-
100
- const startPatternIndex = Math.max(match.index, 0);
101
- const endPatternIndex = startPatternIndex + match[0].length;
102
- const testNameFitsInTerminal = inlineTestName.length <= width;
103
-
104
- if (testNameFitsInTerminal) {
105
- return colorize(inlineTestName, startPatternIndex, endPatternIndex);
106
- }
107
-
108
- const numberOfTruncatedChars = TRIMMING_DOTS.length + inlineTestName.length - width;
109
- const end = Math.max(endPatternIndex - numberOfTruncatedChars, 0);
110
- const truncatedTestName = inlineTestName.slice(numberOfTruncatedChars);
111
- const shouldHighlightDots = startPatternIndex <= numberOfTruncatedChars;
112
-
113
- if (shouldHighlightDots) {
114
- return colorize(TRIMMING_DOTS + truncatedTestName, 0, end + TRIMMING_DOTS.length);
115
- }
116
-
117
- const start = startPatternIndex - numberOfTruncatedChars;
118
- return colorize(TRIMMING_DOTS + truncatedTestName, start + TRIMMING_DOTS.length, end + TRIMMING_DOTS.length);
119
- };
120
- export const removeTrimmingDots = value => {
121
- if (value.startsWith(TRIMMING_DOTS)) {
122
- return value.slice(TRIMMING_DOTS.length);
123
- }
124
-
125
- return value;
126
- };
@@ -1,49 +0,0 @@
1
- import { Prompt } from 'jest-watcher';
2
- import TestNamePatternPrompt from "./prompt.js";
3
- export default class TestNamePlugin {
4
- constructor({
5
- stdin,
6
- stdout,
7
- config = {}
8
- }) {
9
- this._stdin = stdin;
10
- this._stdout = stdout;
11
- this._prompt = new Prompt();
12
- this._testResults = [];
13
- this._usageInfo = {
14
- key: config.key || 't',
15
- prompt: config.prompt || 'filter by a test name regex pattern'
16
- };
17
- }
18
-
19
- apply(jestHooks) {
20
- jestHooks.onTestRunComplete(({
21
- testResults
22
- }) => {
23
- this._testResults = testResults;
24
- });
25
- }
26
-
27
- onKey(key) {
28
- this._prompt.put(key);
29
- }
30
-
31
- run(globalConfig, updateConfigAndRun) {
32
- const p = new TestNamePatternPrompt(this._stdout, this._prompt);
33
- p.updateCachedTestResults(this._testResults);
34
- return new Promise((res, rej) => {
35
- p.run(testNamePattern => {
36
- updateConfigAndRun({
37
- mode: 'watch',
38
- testNamePattern
39
- });
40
- res();
41
- }, rej);
42
- });
43
- }
44
-
45
- getUsageInfo() {
46
- return this._usageInfo;
47
- }
48
-
49
- }
@@ -1,86 +0,0 @@
1
- import chalk from 'chalk';
2
- import ansiEscapes from 'ansi-escapes';
3
- import { PatternPrompt, printPatternCaret, printRestoredPatternCaret } from 'jest-watcher';
4
- import { escapeStrForRegex } from 'jest-regex-util';
5
- import scroll from "../lib/scroll.js";
6
- import { formatTestNameByPattern, getTerminalWidth, removeTrimmingDots } from "../lib/utils.js";
7
- import { formatTypeaheadSelection, printMore, printPatternMatches, printStartTyping, printTypeaheadItem } from "../lib/pattern_mode_helpers.js";
8
- export default class TestNamePatternPrompt extends PatternPrompt {
9
- constructor(pipe, prompt) {
10
- super(pipe, prompt);
11
- this._entityName = 'tests';
12
- this._cachedTestResults = [];
13
- this._offset = -1;
14
- }
15
-
16
- _onChange(pattern, options) {
17
- super._onChange(pattern, options);
18
-
19
- this._offset = options.offset;
20
-
21
- this._printTypeahead(pattern, options);
22
- }
23
-
24
- _printTypeahead(pattern, options) {
25
- const matchedTests = this._getMatchedTests(pattern);
26
-
27
- const total = matchedTests.length;
28
- const pipe = this._pipe;
29
- const prompt = this._prompt;
30
- printPatternCaret(pattern, pipe);
31
- pipe.write(ansiEscapes.cursorLeft);
32
-
33
- if (pattern) {
34
- printPatternMatches(total, 'test', pipe, ` from ${chalk.yellow('cached')} test suites`);
35
- const width = getTerminalWidth(pipe);
36
- const {
37
- start,
38
- end,
39
- index
40
- } = scroll(total, options);
41
- prompt.setPromptLength(total);
42
- matchedTests.slice(start, end).map(name => formatTestNameByPattern(name, pattern, width - 4)).map((item, i) => formatTypeaheadSelection(item, i, index, prompt)).forEach(item => printTypeaheadItem(item, pipe));
43
-
44
- if (total > end) {
45
- printMore('test', pipe, total - end);
46
- }
47
- } else {
48
- printStartTyping('test name', pipe);
49
- }
50
-
51
- printRestoredPatternCaret(pattern, this._currentUsageRows, pipe);
52
- }
53
-
54
- _getMatchedTests(pattern) {
55
- let regex;
56
-
57
- try {
58
- regex = new RegExp(pattern, 'i');
59
- } catch (e) {
60
- return [];
61
- }
62
-
63
- return this._cachedTestResults.reduce((matchedTests, {
64
- testResults
65
- }) => {
66
- return matchedTests.concat(testResults.filter(({
67
- fullName
68
- }) => regex.test(fullName)).map(({
69
- fullName
70
- }) => fullName));
71
- }, []);
72
- }
73
-
74
- updateCachedTestResults(testResults = []) {
75
- this._cachedTestResults = testResults;
76
- }
77
-
78
- run(onSuccess, onCancel, options) {
79
- super.run(value => {
80
- const preparedPattern = escapeStrForRegex(removeTrimmingDots(value));
81
- const useExactMatch = this._offset !== -1;
82
- onSuccess(useExactMatch ? `^${preparedPattern}$` : preparedPattern);
83
- }, onCancel, options);
84
- }
85
-
86
- }
@@ -1 +0,0 @@
1
- export {};