From f925b2214bab1b8a31fc9ac7cfa74023b73325cb Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Sun, 10 May 2020 23:35:40 -0400 Subject: [PATCH 01/11] run prttier for the whole project --- CHANGELOG.md | 2 +- __mocks__/istanbul-lib-source-maps.ts | 2 +- __mocks__/vscode.ts | 140 +- jsconfig.json | 22 +- package.json | 74 +- prettier.config.js | 14 + src/Coverage/CoverageCodeLensProvider.ts | 66 +- src/Coverage/CoverageMapProvider.ts | 68 +- .../Formatters/GutterFormatter/index.ts | 6 +- src/Coverage/index.ts | 6 +- src/DebugCodeLens/DebugCodeLensProvider.ts | 158 +- src/Jest/index.ts | 4 +- src/JestExt.ts | 1030 +++++------ src/JestProcessManagement/JestProcess.ts | 2 +- .../JestProcessManager.ts | 4 +- src/Settings/index.ts | 64 +- .../SnapshotCodeLensProvider.ts | 88 +- .../SnapshotPreviewProvider.ts | 80 +- src/SnapshotCodeLens/index.ts | 4 +- src/StatusBar.ts | 514 +++--- src/TestResults/TestReconciliationState.ts | 18 +- src/TestResults/TestResult.ts | 240 +-- src/TestResults/TestResultProvider.ts | 8 +- src/TestResults/index.ts | 6 +- src/appGlobals.ts | 4 +- src/diagnostics.ts | 12 +- src/extension.ts | 12 +- src/extensionManager.ts | 4 +- src/messaging.ts | 4 +- src/types.ts | 10 +- tests/Coverage/CoverageMapProvider.test.ts | 10 +- .../Formatters/DefaultFormatter.test.ts | 5 +- .../DebugCodeLensProvider.test.ts | 8 +- tests/Jest/index.test.ts | 2 +- tests/JestExt.test.ts | 1598 ++++++++--------- .../JestProcessManagement/JestProcess.test.ts | 2 +- .../JestProcessManager.test.ts | 30 +- tests/TestResults/TestResultProvider.test.ts | 36 +- tests/TestResults/index.test.ts | 18 +- tests/diagnostics.test.ts | 10 +- tests/extension.test.ts | 14 +- tests/extensionManager.test.ts | 16 +- tests/helpers.test.ts | 14 +- 43 files changed, 2243 insertions(+), 2186 deletions(-) create mode 100644 prettier.config.js diff --git a/CHANGELOG.md b/CHANGELOG.md index a61229c6b..c48cb2477 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ Bug-fixes within the same version aren't needed * improve create-react-app detection logic - stephtr * improve the detection of cases in which Jest needs to be restarted with `--watchAll` - [@lordofthelake](https://github.com/lordofthelake) * upgrade all dependencies to the latest, except istanbul-lib-xxx, which requires more code change and will be handled in a separate coverage PR. - @connectdotz - +* run prettier for the whole project and add prettier check in ci script - @connectdotz --> ### 3.2.0 diff --git a/__mocks__/istanbul-lib-source-maps.ts b/__mocks__/istanbul-lib-source-maps.ts index 499cf0114..204c9caec 100644 --- a/__mocks__/istanbul-lib-source-maps.ts +++ b/__mocks__/istanbul-lib-source-maps.ts @@ -1 +1 @@ -export = { createSourceMapStore: jest.fn() } +export = { createSourceMapStore: jest.fn() } diff --git a/__mocks__/vscode.ts b/__mocks__/vscode.ts index 52bf338c5..f952e034c 100644 --- a/__mocks__/vscode.ts +++ b/__mocks__/vscode.ts @@ -1,70 +1,70 @@ -const languages = { - createDiagnosticCollection: jest.fn(), - registerCodeLensProvider: jest.fn(), -} - -const StatusBarAlignment = { Left: 1, Right: 2 } - -const window = { - createStatusBarItem: jest.fn(() => ({ - show: jest.fn(), - tooltip: jest.fn(), - })), - showErrorMessage: jest.fn(), - showWarningMessage: jest.fn(), - createTextEditorDecorationType: jest.fn(), - createOutputChannel: jest.fn(), - showWorkspaceFolderPick: jest.fn(), - onDidChangeActiveTextEditor: jest.fn(), - showInformationMessage: jest.fn(), -} - -const workspace = { - getConfiguration: jest.fn(), - workspaceFolders: [], - getWorkspaceFolder: jest.fn(), - - onDidChangeConfiguration: jest.fn(), - onDidChangeTextDocument: jest.fn(), - onDidChangeWorkspaceFolders: jest.fn(), -} - -const OverviewRulerLane = { - Left: null, -} - -const Uri = { - file: f => f, - parse: jest.fn(), -} -const Range = jest.fn() -const Diagnostic = jest.fn() -const DiagnosticSeverity = { Error: 0, Warning: 1, Information: 2, Hint: 3 } - -const debug = { - onDidTerminateDebugSession: jest.fn(), - startDebugging: jest.fn(), - registerDebugConfigurationProvider: jest.fn(), -} - -const commands = { - executeCommand: jest.fn(), - registerCommand: jest.fn(), -} - -const CodeLens = function CodeLens() {} - -export { - CodeLens, - languages, - StatusBarAlignment, - window, - workspace, - OverviewRulerLane, - Uri, - Range, - Diagnostic, - DiagnosticSeverity, - debug, - commands, -} +const languages = { + createDiagnosticCollection: jest.fn(), + registerCodeLensProvider: jest.fn(), +} + +const StatusBarAlignment = { Left: 1, Right: 2 } + +const window = { + createStatusBarItem: jest.fn(() => ({ + show: jest.fn(), + tooltip: jest.fn(), + })), + showErrorMessage: jest.fn(), + showWarningMessage: jest.fn(), + createTextEditorDecorationType: jest.fn(), + createOutputChannel: jest.fn(), + showWorkspaceFolderPick: jest.fn(), + onDidChangeActiveTextEditor: jest.fn(), + showInformationMessage: jest.fn(), +} + +const workspace = { + getConfiguration: jest.fn(), + workspaceFolders: [], + getWorkspaceFolder: jest.fn(), + + onDidChangeConfiguration: jest.fn(), + onDidChangeTextDocument: jest.fn(), + onDidChangeWorkspaceFolders: jest.fn(), +} + +const OverviewRulerLane = { + Left: null, +} + +const Uri = { + file: (f) => f, + parse: jest.fn(), +} +const Range = jest.fn() +const Diagnostic = jest.fn() +const DiagnosticSeverity = { Error: 0, Warning: 1, Information: 2, Hint: 3 } + +const debug = { + onDidTerminateDebugSession: jest.fn(), + startDebugging: jest.fn(), + registerDebugConfigurationProvider: jest.fn(), +} + +const commands = { + executeCommand: jest.fn(), + registerCommand: jest.fn(), +} + +const CodeLens = function CodeLens() {} + +export { + CodeLens, + languages, + StatusBarAlignment, + window, + workspace, + OverviewRulerLane, + Uri, + Range, + Diagnostic, + DiagnosticSeverity, + debug, + commands, +} diff --git a/jsconfig.json b/jsconfig.json index 0136d6f20..eac02345d 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,16 +1,10 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=759670 - // for the documentation about the jsconfig.json format - "compilerOptions": { - "target": "es6", - "module": "commonjs", - "allowSyntheticDefaultImports": true - }, - "exclude": [ - "node_modules", - "bower_components", - "jspm_packages", - "tmp", - "temp" - ] + // See https://go.microsoft.com/fwlink/?LinkId=759670 + // for the documentation about the jsconfig.json format + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "allowSyntheticDefaultImports": true + }, + "exclude": ["node_modules", "bower_components", "jspm_packages", "tmp", "temp"] } diff --git a/package.json b/package.json index 1083af54e..ca8db183d 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,16 @@ "theme": "dark", "color": "#384357" }, - "categories": ["Other"], - "keywords": ["jest", "debug", "snippet", "react", "multi-root ready"], + "categories": [ + "Other" + ], + "keywords": [ + "jest", + "debug", + "snippet", + "react", + "multi-root ready" + ], "activationEvents": [ "workspaceContains:jest.config.js", "workspaceContains:jest.json", @@ -31,12 +39,19 @@ ], "main": "./out/extension", "icon": "images/vscode-jest.png", - "extensionKind": ["workspace"], + "extensionKind": [ + "workspace" + ], "contributes": { "languages": [ { "id": "jest-snapshot", - "extensions": [".js.snap", ".jsx.snap", ".ts.snap", ".tsx.snap"] + "extensions": [ + ".js.snap", + ".jsx.snap", + ".ts.snap", + ".tsx.snap" + ] } ], "grammars": [ @@ -101,7 +116,10 @@ "jest.coverageFormatter": { "description": "Coverage formatter to use", "type": "string", - "enum": ["DefaultFormatter", "GutterFormatter"], + "enum": [ + "DefaultFormatter", + "GutterFormatter" + ], "default": "DefaultFormatter", "scope": "resource" }, @@ -115,9 +133,17 @@ "description": "Show the debug CodeLens when the it/test block state is in this collection", "type": "array", "items": { - "enum": ["fail", "pass", "skip", "unknown"] + "enum": [ + "fail", + "pass", + "skip", + "unknown" + ] }, - "default": ["fail", "unknown"], + "default": [ + "fail", + "unknown" + ], "scope": "window" }, "jest.enableSnapshotPreviews": { @@ -173,7 +199,12 @@ { "type": "vscode-jest-tests", "label": "Debug Jest tests using vscode-jest", - "languages": ["javascript", "javascriptreact", "typescript", "typescriptreact"], + "languages": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact" + ], "configurationSnippets": [ { "label": "Jest: Default jest configuration", @@ -183,7 +214,9 @@ "name": "vscode-jest-tests", "request": "launch", "program": "^\"\\${workspaceFolder}/node_modules/jest/bin/jest\"", - "args": ["--runInBand"], + "args": [ + "--runInBand" + ], "cwd": "^\"\\${workspaceFolder}\"", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", @@ -198,7 +231,11 @@ "name": "vscode-jest-tests", "request": "launch", "runtimeExecutable": "^\"\\${workspaceFolder}/node_modules/.bin/react-scripts\"", - "args": ["test", "--env=jsdom", "--runInBand"], + "args": [ + "test", + "--env=jsdom", + "--runInBand" + ], "cwd": "^\"\\${workspaceFolder}\"", "console": "integratedTerminal", "protocol": "inspector", @@ -214,7 +251,10 @@ "name": "vscode-jest-tests", "request": "launch", "program": "^\"\\${workspaceFolder}/scripts/test\"", - "args": ["--env=jsdom", "--runInBand"], + "args": [ + "--env=jsdom", + "--runInBand" + ], "cwd": "^\"\\${workspaceFolder}\"", "console": "integratedTerminal", "protocol": "inspector", @@ -227,12 +267,12 @@ ] }, "lint-staged": { - "*.json": "yarn prettier-write --parser json", - "*.ts": "yarn prettier-write --parser typescript" + "*.json": "yarn prettier --write", + "*.ts": "yarn prettier --write" }, "scripts": { "precommit": "lint-staged", - "ci": "yarn lint && yarn test --coverage", + "ci": "yarn lint && yarn prettier-project-check && yarn test --coverage", "clean-out": "rimraf ./out", "vscode:prepublish": "yarn clean-out && yarn compile", "compile": "webpack --mode production", @@ -240,9 +280,9 @@ "lint": "tslint -c tslint.json 'src/**/*.ts' 'tests/**/*.ts'", "test": "jest", "watch-test": "yarn test -- --watch", - "prettier": "prettier --no-semi --single-quote --trailing-comma es5 --print-width 120", - "prettier-write": "yarn prettier --write ", - "prettier-project": "yarn prettier-write --parser typescript -- '?(__mocks__|src|tests)/**/*.ts'" + "prettier": "prettier", + "prettier-project-check": "yarn prettier --check '?(__mocks__|src|tests)/**/*.ts' '*.json'", + "prettier-project-write": "yarn prettier --write '?(__mocks__|src|tests)/**/*.ts' '*.json'" }, "dependencies": { "istanbul-lib-coverage": "^1.1.1", diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 000000000..193379769 --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,14 @@ +module.exports = { + trailingComma: "es5", + printWidth: 120, + semi: false, + singleQuote: true, + "overrides": [ + { + "files": "*.ts", + "options": { + "parser": "typescript" + } + } + ] +}; \ No newline at end of file diff --git a/src/Coverage/CoverageCodeLensProvider.ts b/src/Coverage/CoverageCodeLensProvider.ts index d8b9d0c99..90251d121 100644 --- a/src/Coverage/CoverageCodeLensProvider.ts +++ b/src/Coverage/CoverageCodeLensProvider.ts @@ -1,33 +1,33 @@ -import * as vscode from 'vscode' - -import { GetJestExtByURI } from '../extensionManager' - -export class CoverageCodeLensProvider implements vscode.CodeLensProvider { - private getJestExt: GetJestExtByURI - - constructor(getJestExt: GetJestExtByURI) { - this.getJestExt = getJestExt - } - - public provideCodeLenses(document: vscode.TextDocument, _token: vscode.CancellationToken) { - const ext = this.getJestExt(document.uri) - const coverage = ext && ext.coverageMapProvider.getFileCoverage(document.fileName) - if (!coverage) { - return - } - - const summary = coverage.toSummary() - const json = summary.toJSON() - const metrics = Object.keys(json).reduce((previous, metric) => { - return `${previous}${previous ? ', ' : ''}${metric}: ${json[metric].pct}%` - }, '') - - const range = new vscode.Range(0, 0, 0, 0) - const command: vscode.Command = { - title: metrics, - command: null, - } - - return [new vscode.CodeLens(range, command)] - } -} +import * as vscode from 'vscode' + +import { GetJestExtByURI } from '../extensionManager' + +export class CoverageCodeLensProvider implements vscode.CodeLensProvider { + private getJestExt: GetJestExtByURI + + constructor(getJestExt: GetJestExtByURI) { + this.getJestExt = getJestExt + } + + public provideCodeLenses(document: vscode.TextDocument, _token: vscode.CancellationToken) { + const ext = this.getJestExt(document.uri) + const coverage = ext && ext.coverageMapProvider.getFileCoverage(document.fileName) + if (!coverage) { + return + } + + const summary = coverage.toSummary() + const json = summary.toJSON() + const metrics = Object.keys(json).reduce((previous, metric) => { + return `${previous}${previous ? ', ' : ''}${metric}: ${json[metric].pct}%` + }, '') + + const range = new vscode.Range(0, 0, 0, 0) + const command: vscode.Command = { + title: metrics, + command: null, + } + + return [new vscode.CodeLens(range, command)] + } +} diff --git a/src/Coverage/CoverageMapProvider.ts b/src/Coverage/CoverageMapProvider.ts index 754b590d1..053edf0ee 100644 --- a/src/Coverage/CoverageMapProvider.ts +++ b/src/Coverage/CoverageMapProvider.ts @@ -1,34 +1,34 @@ -import { createSourceMapStore, MapStore } from 'istanbul-lib-source-maps' -import { createCoverageMap, CoverageMap } from 'istanbul-lib-coverage' - -export class CoverageMapProvider { - private mapStore: MapStore - - /** - * Transformed coverage map - */ - private _map: CoverageMap - - constructor() { - this._map = createCoverageMap() - this.mapStore = createSourceMapStore() - } - - get map(): CoverageMap { - return this._map - } - - update(obj: CoverageMap | object) { - const map = createCoverageMap(obj) - const transformed = this.mapStore.transformCoverage(map) - if (this._map) { - this._map.merge(transformed.map) - } else { - this._map = transformed.map - } - } - - public getFileCoverage(filePath: string) { - return this._map.data[filePath] - } -} +import { createSourceMapStore, MapStore } from 'istanbul-lib-source-maps' +import { createCoverageMap, CoverageMap } from 'istanbul-lib-coverage' + +export class CoverageMapProvider { + private mapStore: MapStore + + /** + * Transformed coverage map + */ + private _map: CoverageMap + + constructor() { + this._map = createCoverageMap() + this.mapStore = createSourceMapStore() + } + + get map(): CoverageMap { + return this._map + } + + update(obj: CoverageMap | object) { + const map = createCoverageMap(obj) + const transformed = this.mapStore.transformCoverage(map) + if (this._map) { + this._map.merge(transformed.map) + } else { + this._map = transformed.map + } + } + + public getFileCoverage(filePath: string) { + return this._map.data[filePath] + } +} diff --git a/src/Coverage/Formatters/GutterFormatter/index.ts b/src/Coverage/Formatters/GutterFormatter/index.ts index 7bae32f30..b34d45a82 100644 --- a/src/Coverage/Formatters/GutterFormatter/index.ts +++ b/src/Coverage/Formatters/GutterFormatter/index.ts @@ -75,7 +75,7 @@ export class GutterFormatter extends AbstractFormatter { } } - Object.keys(fileCoverage.b).forEach(branchIndex => { + Object.keys(fileCoverage.b).forEach((branchIndex) => { fileCoverage.b[branchIndex].forEach((hitCount, locationIndex) => { if (hitCount > 0) { return @@ -87,8 +87,8 @@ export class GutterFormatter extends AbstractFormatter { } const partialLineRange = new vscode.Range(branch.start.line - 1, 0, branch.start.line - 1, 0) - coverageFormatting.covered = coverageFormatting.covered.filter(range => !range.isEqual(partialLineRange)) - coverageFormatting.uncovered = coverageFormatting.uncovered.filter(range => !range.isEqual(partialLineRange)) + coverageFormatting.covered = coverageFormatting.covered.filter((range) => !range.isEqual(partialLineRange)) + coverageFormatting.uncovered = coverageFormatting.uncovered.filter((range) => !range.isEqual(partialLineRange)) coverageFormatting.partiallyCovered.push( new vscode.Range(branch.start.line - 1, branch.start.column, branch.end.line - 1, branch.end.column) diff --git a/src/Coverage/index.ts b/src/Coverage/index.ts index 9f821166d..af36d6511 100644 --- a/src/Coverage/index.ts +++ b/src/Coverage/index.ts @@ -1,3 +1,3 @@ -export * from './CoverageMapProvider' -export * from './CoverageOverlay' -export * from './CoverageCodeLensProvider' +export * from './CoverageMapProvider' +export * from './CoverageOverlay' +export * from './CoverageCodeLensProvider' diff --git a/src/DebugCodeLens/DebugCodeLensProvider.ts b/src/DebugCodeLens/DebugCodeLensProvider.ts index 519a9eb6c..6b9466a3a 100644 --- a/src/DebugCodeLens/DebugCodeLensProvider.ts +++ b/src/DebugCodeLens/DebugCodeLensProvider.ts @@ -1,79 +1,79 @@ -import * as vscode from 'vscode' -import { extensionName } from '../appGlobals' -import { escapeRegExp } from '../helpers' -import { basename } from 'path' -import { DebugCodeLens } from './DebugCodeLens' -import { TestReconciliationState } from '../TestResults' -import { TestState, TestStateByTestReconciliationState } from './TestState' -import { GetJestExtByURI } from '../extensionManager' - -export class DebugCodeLensProvider implements vscode.CodeLensProvider { - onDidChange: vscode.EventEmitter<void> - private _showWhenTestStateIn: TestState[] - private getJestExt: GetJestExtByURI - - constructor(getJestExt: GetJestExtByURI, showWhenTestStateIn: TestState[] = []) { - this.getJestExt = getJestExt - this._showWhenTestStateIn = showWhenTestStateIn - this.onDidChange = new vscode.EventEmitter() - } - - get showWhenTestStateIn() { - return this._showWhenTestStateIn - } - - set showWhenTestStateIn(value: TestState[]) { - this._showWhenTestStateIn = value - this.onDidChange.fire() - } - - get onDidChangeCodeLenses(): vscode.Event<void> { - return this.onDidChange.event - } - - provideCodeLenses(document: vscode.TextDocument, _: vscode.CancellationToken): vscode.CodeLens[] { - const result = [] - const ext = this.getJestExt(document.uri) - if (!ext || this._showWhenTestStateIn.length === 0 || document.isUntitled) { - return result - } - - const filePath = document.fileName - const testResults = ext.testResultProvider.getResults(filePath) - const fileName = basename(document.fileName) - - for (const test of testResults) { - if (!this.showCodeLensAboveTest(test)) { - continue - } - - const start = new vscode.Position(test.start.line, test.start.column) - const end = new vscode.Position(test.end.line, test.start.column + 5) - const range = new vscode.Range(start, end) - result.push(new DebugCodeLens(document, range, fileName, test.name)) - } - - return result - } - - showCodeLensAboveTest(test: { status: TestReconciliationState }) { - const state = TestStateByTestReconciliationState[test.status] - return this._showWhenTestStateIn.includes(state) - } - - resolveCodeLens(codeLens: vscode.CodeLens, _: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens> { - if (codeLens instanceof DebugCodeLens) { - codeLens.command = { - arguments: [codeLens.document, codeLens.fileName, escapeRegExp(codeLens.testName)], - command: `${extensionName}.run-test`, - title: 'Debug', - } - } - - return codeLens - } - - didChange() { - this.onDidChange.fire() - } -} +import * as vscode from 'vscode' +import { extensionName } from '../appGlobals' +import { escapeRegExp } from '../helpers' +import { basename } from 'path' +import { DebugCodeLens } from './DebugCodeLens' +import { TestReconciliationState } from '../TestResults' +import { TestState, TestStateByTestReconciliationState } from './TestState' +import { GetJestExtByURI } from '../extensionManager' + +export class DebugCodeLensProvider implements vscode.CodeLensProvider { + onDidChange: vscode.EventEmitter<void> + private _showWhenTestStateIn: TestState[] + private getJestExt: GetJestExtByURI + + constructor(getJestExt: GetJestExtByURI, showWhenTestStateIn: TestState[] = []) { + this.getJestExt = getJestExt + this._showWhenTestStateIn = showWhenTestStateIn + this.onDidChange = new vscode.EventEmitter() + } + + get showWhenTestStateIn() { + return this._showWhenTestStateIn + } + + set showWhenTestStateIn(value: TestState[]) { + this._showWhenTestStateIn = value + this.onDidChange.fire() + } + + get onDidChangeCodeLenses(): vscode.Event<void> { + return this.onDidChange.event + } + + provideCodeLenses(document: vscode.TextDocument, _: vscode.CancellationToken): vscode.CodeLens[] { + const result = [] + const ext = this.getJestExt(document.uri) + if (!ext || this._showWhenTestStateIn.length === 0 || document.isUntitled) { + return result + } + + const filePath = document.fileName + const testResults = ext.testResultProvider.getResults(filePath) + const fileName = basename(document.fileName) + + for (const test of testResults) { + if (!this.showCodeLensAboveTest(test)) { + continue + } + + const start = new vscode.Position(test.start.line, test.start.column) + const end = new vscode.Position(test.end.line, test.start.column + 5) + const range = new vscode.Range(start, end) + result.push(new DebugCodeLens(document, range, fileName, test.name)) + } + + return result + } + + showCodeLensAboveTest(test: { status: TestReconciliationState }) { + const state = TestStateByTestReconciliationState[test.status] + return this._showWhenTestStateIn.includes(state) + } + + resolveCodeLens(codeLens: vscode.CodeLens, _: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens> { + if (codeLens instanceof DebugCodeLens) { + codeLens.command = { + arguments: [codeLens.document, codeLens.fileName, escapeRegExp(codeLens.testName)], + command: `${extensionName}.run-test`, + title: 'Debug', + } + } + + return codeLens + } + + didChange() { + this.onDidChange.fire() + } +} diff --git a/src/Jest/index.ts b/src/Jest/index.ts index 3db099bb8..72166fe8a 100644 --- a/src/Jest/index.ts +++ b/src/Jest/index.ts @@ -4,8 +4,8 @@ export enum WatchMode { WatchAll = 'watchAll', } -const IS_OUTSIDE_REPOSITORY_REGEXP = /Test suite failed to run[\s\S]*fatal:[\s\S]*is outside repository/im; -const WATCH_IS_NOT_SUPPORTED_REGEXP = /^s*--watch is not supported without git\/hg, please use --watchAlls*/im; +const IS_OUTSIDE_REPOSITORY_REGEXP = /Test suite failed to run[\s\S]*fatal:[\s\S]*is outside repository/im +const WATCH_IS_NOT_SUPPORTED_REGEXP = /^s*--watch is not supported without git\/hg, please use --watchAlls*/im export const isWatchNotSupported = (str = '') => IS_OUTSIDE_REPOSITORY_REGEXP.test(str) || WATCH_IS_NOT_SUPPORTED_REGEXP.test(str) diff --git a/src/JestExt.ts b/src/JestExt.ts index 07c2dd1e3..b27762705 100644 --- a/src/JestExt.ts +++ b/src/JestExt.ts @@ -1,515 +1,515 @@ -import * as vscode from 'vscode' -import { ProjectWorkspace, JestTotalResults } from 'jest-editor-support' - -import * as decorations from './decorations' -import { IPluginResourceSettings } from './Settings' -import { statusBar, Status, StatusBar, Mode } from './StatusBar' -import { - TestReconciliationState, - TestResultProvider, - TestResult, - resultsWithLowerCaseWindowsDriveLetters, - SortedTestResults, -} from './TestResults' -import { pathToJest, pathToConfig, cleanAnsi } from './helpers' -import { CoverageMapProvider } from './Coverage' -import { updateDiagnostics, updateCurrentDiagnostics, resetDiagnostics, failedSuiteCount } from './diagnostics' -import { DebugCodeLensProvider } from './DebugCodeLens' -import { DebugConfigurationProvider } from './DebugConfigurationProvider' -import { DecorationOptions } from './types' -import { isOpenInMultipleEditors } from './editor' -import { CoverageOverlay } from './Coverage/CoverageOverlay' -import { JestProcess, JestProcessManager } from './JestProcessManagement' -import { isWatchNotSupported, WatchMode } from './Jest' -import * as messaging from './messaging' -import { resultsWithoutAnsiEscapeSequence } from './TestResults/TestResult' - -interface InstanceSettings { - multirootEnv: boolean -} - -export class JestExt { - coverageMapProvider: CoverageMapProvider - coverageOverlay: CoverageOverlay - - testResultProvider: TestResultProvider - debugCodeLensProvider: DebugCodeLensProvider - debugConfigurationProvider: DebugConfigurationProvider - - // So you can read what's going on - channel: vscode.OutputChannel - - failingAssertionDecorators: { [fileName: string]: vscode.TextEditorDecorationType[] } - - private jestWorkspace: ProjectWorkspace - private pluginSettings: IPluginResourceSettings - private workspaceFolder: vscode.WorkspaceFolder - private instanceSettings: InstanceSettings - - // The ability to show fails in the problems section - private failDiagnostics: vscode.DiagnosticCollection - - private passingItStyle: vscode.TextEditorDecorationType - private failingItStyle: vscode.TextEditorDecorationType - private skipItStyle: vscode.TextEditorDecorationType - private unknownItStyle: vscode.TextEditorDecorationType - - private parsingTestFile = false - - // We have to keep track of our inline assert fails to remove later - - private jestProcessManager: JestProcessManager - private jestProcess: JestProcess - - private status: ReturnType<StatusBar['bind']> - - constructor( - context: vscode.ExtensionContext, - workspaceFolder: vscode.WorkspaceFolder, - jestWorkspace: ProjectWorkspace, - outputChannel: vscode.OutputChannel, - pluginSettings: IPluginResourceSettings, - debugCodeLensProvider: DebugCodeLensProvider, - debugConfigurationProvider: DebugConfigurationProvider, - failDiagnostics: vscode.DiagnosticCollection, - instanceSettings: InstanceSettings - ) { - this.workspaceFolder = workspaceFolder - this.jestWorkspace = jestWorkspace - this.channel = outputChannel - this.failingAssertionDecorators = {} - this.failDiagnostics = failDiagnostics - this.pluginSettings = pluginSettings - this.debugCodeLensProvider = debugCodeLensProvider - this.instanceSettings = instanceSettings - - this.coverageMapProvider = new CoverageMapProvider() - this.coverageOverlay = new CoverageOverlay( - context, - this.coverageMapProvider, - pluginSettings.showCoverageOnLoad, - pluginSettings.coverageFormatter - ) - this.jestWorkspace.collectCoverage = pluginSettings.showCoverageOnLoad - - this.testResultProvider = new TestResultProvider(this.pluginSettings.debugMode) - this.debugConfigurationProvider = debugConfigurationProvider - - this.jestProcessManager = new JestProcessManager({ - projectWorkspace: jestWorkspace, - runAllTestsFirstInWatchMode: this.pluginSettings.runAllTestsFirst, - }) - - this.status = statusBar.bind(workspaceFolder.name) - this.handleJestEditorSupportEvent = this.handleJestEditorSupportEvent.bind(this) - - // The theme stuff - this.setupDecorators() - // The bottom bar thing - this.setupStatusBar() - // reset the jest diagnostics - resetDiagnostics(this.failDiagnostics) - - // If we should start the process by default, do so - if (this.pluginSettings.autoEnable) { - this.startProcess() - } else { - this.channel.appendLine('Skipping initial Jest runner process start.') - } - } - - public startProcess() { - if (this.jestProcessManager.numberOfProcesses > 0) { - // tslint:disable-next-line no-console - console.warn(`process is already running, will not start a new process.`) - return - } - - this.jestProcess = this.jestProcessManager.startJestProcess({ - watchMode: WatchMode.Watch, - keepAlive: true, - exitCallback: (jestProcess, jestProcessInWatchMode) => { - if (jestProcessInWatchMode) { - this.jestProcess = jestProcessInWatchMode - - this.channel.appendLine('Finished running all tests. Starting watch mode.') - this.updateStatusBar('running', 'Starting watch mode', false) - - this.assignHandlers(this.jestProcess) - } else { - this.updateStatusBar('stopped', undefined, false) - if (!jestProcess.stopRequested()) { - let msg = 'Starting Jest in Watch mode failed too many times and has been stopped.' - if (this.instanceSettings.multirootEnv) { - const folder = this.workspaceFolder.name - msg = `(${folder}) ${msg}\nIf this is expected, consider adding '${folder}' to disabledWorkspaceFolders` - } - this.channel.appendLine(`${msg}\n see troubleshooting: ${messaging.TROUBLESHOOTING_URL}`) - this.channel.show(true) - messaging.systemErrorMessage(msg, messaging.showTroubleshootingAction) - } - } - }, - }) - - this.assignHandlers(this.jestProcess) - } - - public stopProcess() { - this.channel.appendLine('Closing Jest') - return this.jestProcessManager.stopAll().then(() => { - this.updateStatusBar('stopped') - }) - } - - public restartProcess() { - return this.stopProcess().then(() => { - this.startProcess() - }) - } - - public triggerUpdateActiveEditor(editor: vscode.TextEditor) { - this.coverageOverlay.updateVisibleEditors() - - if (!this.canUpdateActiveEditor(editor)) { - return - } - - // not sure why we need to protect this block with parsingTestFile ? - // using an ivar as a locking mechanism has bad smell - // TODO: refactor maybe? - this.parsingTestFile = true - - const filePath = editor.document.fileName - const testResults = this.testResultProvider.getSortedResults(filePath) - - this.updateDecorators(testResults, editor) - updateCurrentDiagnostics(testResults.fail, this.failDiagnostics, editor) - - this.parsingTestFile = false - } - - public triggerUpdateSettings(updatedSettings: IPluginResourceSettings) { - this.pluginSettings = updatedSettings - - this.jestWorkspace.rootPath = updatedSettings.rootPath - this.jestWorkspace.pathToJest = pathToJest(updatedSettings) - this.jestWorkspace.pathToConfig = pathToConfig(updatedSettings) - - // debug - this.jestWorkspace.debug = updatedSettings.debugMode - this.testResultProvider.verbose = updatedSettings.debugMode - - // coverage - const showCoverage = - this.coverageOverlay.enabled === undefined ? updatedSettings.showCoverageOnLoad : this.coverageOverlay.enabled - this.jestWorkspace.collectCoverage = showCoverage - this.coverageOverlay.enabled = showCoverage - - this.restartProcess() - } - - updateDecorators(testResults: SortedTestResults, editor: vscode.TextEditor) { - // Dots - const styleMap = [ - { data: testResults.success, decorationType: this.passingItStyle, state: TestReconciliationState.KnownSuccess }, - { data: testResults.fail, decorationType: this.failingItStyle, state: TestReconciliationState.KnownFail }, - { data: testResults.skip, decorationType: this.skipItStyle, state: TestReconciliationState.KnownSkip }, - { data: testResults.unknown, decorationType: this.unknownItStyle, state: TestReconciliationState.Unknown }, - ] - - styleMap.forEach(style => { - const decorators = this.generateDotsForItBlocks(style.data, style.state) - editor.setDecorations(style.decorationType, decorators) - }) - - // Debug CodeLens - this.debugCodeLensProvider.didChange() - - // Inline error messages - this.resetInlineErrorDecorators(editor) - if (this.pluginSettings.enableInlineErrorMessages) { - const fileName = editor.document.fileName - testResults.fail.forEach(a => { - const { style, decorator } = this.generateInlineErrorDecorator(fileName, a) - editor.setDecorations(style, [decorator]) - }) - } - } - - canUpdateActiveEditor(editor: vscode.TextEditor) { - const inSettings = !editor.document - if (inSettings) { - return false - } - - if (this.parsingTestFile) { - return false - } - - // check if file is a possible code file: js/jsx/ts/tsx - const codeRegex = /\.[t|j]sx?$/ - return codeRegex.test(editor.document.uri.fsPath) - } - - public deactivate() { - this.jestProcessManager.stopAll() - } - - public runTest = async (workspaceFolder: vscode.WorkspaceFolder, fileName: string, identifier: string) => { - const restart = this.jestProcessManager.numberOfProcesses > 0 - this.jestProcessManager.stopAll() - - this.debugConfigurationProvider.prepareTestRun(fileName, identifier) - - const handle = vscode.debug.onDidTerminateDebugSession(_ => { - handle.dispose() - if (restart) { - this.startProcess() - } - }) - - try { - // try to run the debug configuration from launch.json - await vscode.debug.startDebugging(workspaceFolder, 'vscode-jest-tests') - } catch { - // if that fails, there (probably) isn't any debug configuration (at least no correctly named one) - // therefore debug the test using the default configuration - const debugConfiguration = this.debugConfigurationProvider.provideDebugConfigurations(workspaceFolder)[0] - await vscode.debug.startDebugging(workspaceFolder, debugConfiguration) - } - } - - onDidCloseTextDocument(document: vscode.TextDocument) { - this.removeCachedTestResults(document) - this.removeCachedDecorationTypes(document) - } - - removeCachedTestResults(document: vscode.TextDocument) { - if (!document || document.isUntitled) { - return - } - - const filePath = document.fileName - this.testResultProvider.removeCachedResults(filePath) - } - - removeCachedDecorationTypes(document: vscode.TextDocument) { - if (!document || !document.fileName) { - return - } - - delete this.failingAssertionDecorators[document.fileName] - } - - onDidChangeActiveTextEditor(editor: vscode.TextEditor) { - this.triggerUpdateActiveEditor(editor) - } - - /** - * This event is fired with the document not dirty when: - * - before the onDidSaveTextDocument event - * - the document was changed by an external editor - */ - onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { - if (event.document.isDirty) { - return - } - if (event.document.uri.scheme === 'git') { - return - } - - // Ignore a clean file with a change: - if (event.contentChanges.length > 0) { - return - } - - this.removeCachedTestResults(event.document) - - for (const editor of vscode.window.visibleTextEditors) { - if (editor.document === event.document) { - this.triggerUpdateActiveEditor(editor) - } - } - } - - toggleCoverageOverlay() { - this.coverageOverlay.toggleVisibility() - - // restart jest since coverage condition has changed - this.triggerUpdateSettings(this.pluginSettings) - } - - private detectedSnapshotErrors() { - if (!this.pluginSettings.enableSnapshotUpdateMessages) { - return - } - vscode.window - .showInformationMessage('Would you like to update your Snapshots?', { title: 'Replace them' }) - .then(response => { - // No response == cancel - if (response) { - this.jestProcess.runJestWithUpdateForSnapshots(() => { - if (this.pluginSettings.restartJestOnSnapshotUpdate) { - this.jestProcessManager.stopJestProcess(this.jestProcess).then(() => { - this.startProcess() - }) - vscode.window.showInformationMessage('Updated Snapshots and restarted Jest.') - } else { - vscode.window.showInformationMessage('Updated Snapshots. It will show in your next test run.') - } - }) - } - }) - } - - private resetInlineErrorDecorators(editor: vscode.TextEditor) { - if (!this.failingAssertionDecorators[editor.document.fileName]) { - this.failingAssertionDecorators[editor.document.fileName] = [] - return - } - - if (isOpenInMultipleEditors(editor.document)) { - return - } - - this.failingAssertionDecorators[editor.document.fileName].forEach(element => { - element.dispose() - }) - this.failingAssertionDecorators[editor.document.fileName] = [] - } - - private generateInlineErrorDecorator(fileName: string, test: TestResult) { - const errorMessage = test.terseMessage || test.shortMessage - const decorator = { - range: new vscode.Range(test.lineNumberOfError, 0, test.lineNumberOfError, 0), - } - - // We have to make a new style for each unique message, this is - // why we have to remove off of them beforehand - const style = decorations.failingAssertionStyle(errorMessage) - this.failingAssertionDecorators[fileName].push(style) - - return { style, decorator } - } - - private handleStdErr(error: Buffer) { - const message = error.toString() - if (this.shouldIgnoreOutput(message)) { - return - } - - if (isWatchNotSupported(message)) { - this.jestProcess.watchMode = WatchMode.WatchAll - } - - const noANSI = cleanAnsi(message) - if (/(snapshots? failed)|(snapshot test failed)/i.test(noANSI)) { - this.detectedSnapshotErrors() - } - - this.channel.appendLine(noANSI) - } - - private handleJestEditorSupportEvent(output: string) { - if (output.includes('onRunStart')) { - this.channel.clear() - this.updateStatusBar('running', 'Running tests', false) - } - if (output.includes('onRunComplete')) { - this.updateStatusBar('stopped', undefined, false) - this.parsingTestFile = false - } - - if (!this.shouldIgnoreOutput(output)) { - this.channel.appendLine(output) - } - } - - private assignHandlers(jestProcess: JestProcess) { - jestProcess - .onJestEditorSupportEvent('executableJSON', (data: JestTotalResults) => { - this.updateWithData(data) - }) - .onJestEditorSupportEvent('executableOutput', this.handleJestEditorSupportEvent) - .onJestEditorSupportEvent('executableStdErr', (error: Buffer) => this.handleStdErr(error)) - .onJestEditorSupportEvent('nonTerminalError', (error: string) => { - this.channel.appendLine(`Received an error from Jest Runner: ${error.toString()}`) - this.channel.show(true) - }) - .onJestEditorSupportEvent('exception', result => { - this.channel.appendLine(`\nException raised: [${result.type}]: ${result.message}\n`) - this.channel.show(true) - }) - .onJestEditorSupportEvent('terminalError', (error: string) => { - this.channel.appendLine('\nException raised: ' + error) - this.channel.show(true) - }) - } - - private setupStatusBar() { - this.updateStatusBar('initial', undefined, false) - } - - private updateStatusBar(status: Status, details?: string, watchMode: boolean = true) { - const modes: Mode[] = [] - if (this.coverageOverlay.enabled) { - modes.push('coverage') - } - if (watchMode) { - modes.push('watch') - } - this.status.update(status, details, modes) - } - - private setupDecorators() { - this.passingItStyle = decorations.passingItName() - this.failingItStyle = decorations.failingItName() - this.skipItStyle = decorations.skipItName() - this.unknownItStyle = decorations.notRanItName() - } - - private shouldIgnoreOutput(text: string): boolean { - // this fails when snapshots change - to be revised - returning always false for now - return text.includes('Watch Usage') || text.includes('onRunComplete') || text.includes('onRunStart') - } - - private updateWithData(data: JestTotalResults) { - const noAnsiData = resultsWithoutAnsiEscapeSequence(data) - const normalizedData = resultsWithLowerCaseWindowsDriveLetters(noAnsiData) - this.coverageMapProvider.update(normalizedData.coverageMap) - - const statusList = this.testResultProvider.updateTestResults(normalizedData) - updateDiagnostics(statusList, this.failDiagnostics) - - const failedFileCount = failedSuiteCount(this.failDiagnostics) - if (failedFileCount <= 0 && normalizedData.success) { - this.updateStatusBar('success') - } else { - this.updateStatusBar('failed', ` (${failedFileCount} test suite${failedFileCount > 1 ? 's' : ''} failed)`) - } - - for (const editor of vscode.window.visibleTextEditors) { - if (vscode.workspace.getWorkspaceFolder(editor.document.uri) === this.workspaceFolder) { - this.triggerUpdateActiveEditor(editor) - } - } - } - - private generateDotsForItBlocks(blocks: TestResult[], state: TestReconciliationState): DecorationOptions[] { - const nameForState = { - [TestReconciliationState.KnownSuccess]: 'Passed', - [TestReconciliationState.KnownFail]: 'Failed', - [TestReconciliationState.KnownSkip]: 'Skipped', - [TestReconciliationState.Unknown]: 'Test has not run yet, due to Jest only running tests related to changes.', - } - - return blocks.map(it => { - return { - range: new vscode.Range(it.start.line, it.start.column, it.start.line, it.start.column + 1), - hoverMessage: nameForState[state], - identifier: it.name, - } - }) - } -} +import * as vscode from 'vscode' +import { ProjectWorkspace, JestTotalResults } from 'jest-editor-support' + +import * as decorations from './decorations' +import { IPluginResourceSettings } from './Settings' +import { statusBar, Status, StatusBar, Mode } from './StatusBar' +import { + TestReconciliationState, + TestResultProvider, + TestResult, + resultsWithLowerCaseWindowsDriveLetters, + SortedTestResults, +} from './TestResults' +import { pathToJest, pathToConfig, cleanAnsi } from './helpers' +import { CoverageMapProvider } from './Coverage' +import { updateDiagnostics, updateCurrentDiagnostics, resetDiagnostics, failedSuiteCount } from './diagnostics' +import { DebugCodeLensProvider } from './DebugCodeLens' +import { DebugConfigurationProvider } from './DebugConfigurationProvider' +import { DecorationOptions } from './types' +import { isOpenInMultipleEditors } from './editor' +import { CoverageOverlay } from './Coverage/CoverageOverlay' +import { JestProcess, JestProcessManager } from './JestProcessManagement' +import { isWatchNotSupported, WatchMode } from './Jest' +import * as messaging from './messaging' +import { resultsWithoutAnsiEscapeSequence } from './TestResults/TestResult' + +interface InstanceSettings { + multirootEnv: boolean +} + +export class JestExt { + coverageMapProvider: CoverageMapProvider + coverageOverlay: CoverageOverlay + + testResultProvider: TestResultProvider + debugCodeLensProvider: DebugCodeLensProvider + debugConfigurationProvider: DebugConfigurationProvider + + // So you can read what's going on + channel: vscode.OutputChannel + + failingAssertionDecorators: { [fileName: string]: vscode.TextEditorDecorationType[] } + + private jestWorkspace: ProjectWorkspace + private pluginSettings: IPluginResourceSettings + private workspaceFolder: vscode.WorkspaceFolder + private instanceSettings: InstanceSettings + + // The ability to show fails in the problems section + private failDiagnostics: vscode.DiagnosticCollection + + private passingItStyle: vscode.TextEditorDecorationType + private failingItStyle: vscode.TextEditorDecorationType + private skipItStyle: vscode.TextEditorDecorationType + private unknownItStyle: vscode.TextEditorDecorationType + + private parsingTestFile = false + + // We have to keep track of our inline assert fails to remove later + + private jestProcessManager: JestProcessManager + private jestProcess: JestProcess + + private status: ReturnType<StatusBar['bind']> + + constructor( + context: vscode.ExtensionContext, + workspaceFolder: vscode.WorkspaceFolder, + jestWorkspace: ProjectWorkspace, + outputChannel: vscode.OutputChannel, + pluginSettings: IPluginResourceSettings, + debugCodeLensProvider: DebugCodeLensProvider, + debugConfigurationProvider: DebugConfigurationProvider, + failDiagnostics: vscode.DiagnosticCollection, + instanceSettings: InstanceSettings + ) { + this.workspaceFolder = workspaceFolder + this.jestWorkspace = jestWorkspace + this.channel = outputChannel + this.failingAssertionDecorators = {} + this.failDiagnostics = failDiagnostics + this.pluginSettings = pluginSettings + this.debugCodeLensProvider = debugCodeLensProvider + this.instanceSettings = instanceSettings + + this.coverageMapProvider = new CoverageMapProvider() + this.coverageOverlay = new CoverageOverlay( + context, + this.coverageMapProvider, + pluginSettings.showCoverageOnLoad, + pluginSettings.coverageFormatter + ) + this.jestWorkspace.collectCoverage = pluginSettings.showCoverageOnLoad + + this.testResultProvider = new TestResultProvider(this.pluginSettings.debugMode) + this.debugConfigurationProvider = debugConfigurationProvider + + this.jestProcessManager = new JestProcessManager({ + projectWorkspace: jestWorkspace, + runAllTestsFirstInWatchMode: this.pluginSettings.runAllTestsFirst, + }) + + this.status = statusBar.bind(workspaceFolder.name) + this.handleJestEditorSupportEvent = this.handleJestEditorSupportEvent.bind(this) + + // The theme stuff + this.setupDecorators() + // The bottom bar thing + this.setupStatusBar() + // reset the jest diagnostics + resetDiagnostics(this.failDiagnostics) + + // If we should start the process by default, do so + if (this.pluginSettings.autoEnable) { + this.startProcess() + } else { + this.channel.appendLine('Skipping initial Jest runner process start.') + } + } + + public startProcess() { + if (this.jestProcessManager.numberOfProcesses > 0) { + // tslint:disable-next-line no-console + console.warn(`process is already running, will not start a new process.`) + return + } + + this.jestProcess = this.jestProcessManager.startJestProcess({ + watchMode: WatchMode.Watch, + keepAlive: true, + exitCallback: (jestProcess, jestProcessInWatchMode) => { + if (jestProcessInWatchMode) { + this.jestProcess = jestProcessInWatchMode + + this.channel.appendLine('Finished running all tests. Starting watch mode.') + this.updateStatusBar('running', 'Starting watch mode', false) + + this.assignHandlers(this.jestProcess) + } else { + this.updateStatusBar('stopped', undefined, false) + if (!jestProcess.stopRequested()) { + let msg = 'Starting Jest in Watch mode failed too many times and has been stopped.' + if (this.instanceSettings.multirootEnv) { + const folder = this.workspaceFolder.name + msg = `(${folder}) ${msg}\nIf this is expected, consider adding '${folder}' to disabledWorkspaceFolders` + } + this.channel.appendLine(`${msg}\n see troubleshooting: ${messaging.TROUBLESHOOTING_URL}`) + this.channel.show(true) + messaging.systemErrorMessage(msg, messaging.showTroubleshootingAction) + } + } + }, + }) + + this.assignHandlers(this.jestProcess) + } + + public stopProcess() { + this.channel.appendLine('Closing Jest') + return this.jestProcessManager.stopAll().then(() => { + this.updateStatusBar('stopped') + }) + } + + public restartProcess() { + return this.stopProcess().then(() => { + this.startProcess() + }) + } + + public triggerUpdateActiveEditor(editor: vscode.TextEditor) { + this.coverageOverlay.updateVisibleEditors() + + if (!this.canUpdateActiveEditor(editor)) { + return + } + + // not sure why we need to protect this block with parsingTestFile ? + // using an ivar as a locking mechanism has bad smell + // TODO: refactor maybe? + this.parsingTestFile = true + + const filePath = editor.document.fileName + const testResults = this.testResultProvider.getSortedResults(filePath) + + this.updateDecorators(testResults, editor) + updateCurrentDiagnostics(testResults.fail, this.failDiagnostics, editor) + + this.parsingTestFile = false + } + + public triggerUpdateSettings(updatedSettings: IPluginResourceSettings) { + this.pluginSettings = updatedSettings + + this.jestWorkspace.rootPath = updatedSettings.rootPath + this.jestWorkspace.pathToJest = pathToJest(updatedSettings) + this.jestWorkspace.pathToConfig = pathToConfig(updatedSettings) + + // debug + this.jestWorkspace.debug = updatedSettings.debugMode + this.testResultProvider.verbose = updatedSettings.debugMode + + // coverage + const showCoverage = + this.coverageOverlay.enabled === undefined ? updatedSettings.showCoverageOnLoad : this.coverageOverlay.enabled + this.jestWorkspace.collectCoverage = showCoverage + this.coverageOverlay.enabled = showCoverage + + this.restartProcess() + } + + updateDecorators(testResults: SortedTestResults, editor: vscode.TextEditor) { + // Dots + const styleMap = [ + { data: testResults.success, decorationType: this.passingItStyle, state: TestReconciliationState.KnownSuccess }, + { data: testResults.fail, decorationType: this.failingItStyle, state: TestReconciliationState.KnownFail }, + { data: testResults.skip, decorationType: this.skipItStyle, state: TestReconciliationState.KnownSkip }, + { data: testResults.unknown, decorationType: this.unknownItStyle, state: TestReconciliationState.Unknown }, + ] + + styleMap.forEach((style) => { + const decorators = this.generateDotsForItBlocks(style.data, style.state) + editor.setDecorations(style.decorationType, decorators) + }) + + // Debug CodeLens + this.debugCodeLensProvider.didChange() + + // Inline error messages + this.resetInlineErrorDecorators(editor) + if (this.pluginSettings.enableInlineErrorMessages) { + const fileName = editor.document.fileName + testResults.fail.forEach((a) => { + const { style, decorator } = this.generateInlineErrorDecorator(fileName, a) + editor.setDecorations(style, [decorator]) + }) + } + } + + canUpdateActiveEditor(editor: vscode.TextEditor) { + const inSettings = !editor.document + if (inSettings) { + return false + } + + if (this.parsingTestFile) { + return false + } + + // check if file is a possible code file: js/jsx/ts/tsx + const codeRegex = /\.[t|j]sx?$/ + return codeRegex.test(editor.document.uri.fsPath) + } + + public deactivate() { + this.jestProcessManager.stopAll() + } + + public runTest = async (workspaceFolder: vscode.WorkspaceFolder, fileName: string, identifier: string) => { + const restart = this.jestProcessManager.numberOfProcesses > 0 + this.jestProcessManager.stopAll() + + this.debugConfigurationProvider.prepareTestRun(fileName, identifier) + + const handle = vscode.debug.onDidTerminateDebugSession((_) => { + handle.dispose() + if (restart) { + this.startProcess() + } + }) + + try { + // try to run the debug configuration from launch.json + await vscode.debug.startDebugging(workspaceFolder, 'vscode-jest-tests') + } catch { + // if that fails, there (probably) isn't any debug configuration (at least no correctly named one) + // therefore debug the test using the default configuration + const debugConfiguration = this.debugConfigurationProvider.provideDebugConfigurations(workspaceFolder)[0] + await vscode.debug.startDebugging(workspaceFolder, debugConfiguration) + } + } + + onDidCloseTextDocument(document: vscode.TextDocument) { + this.removeCachedTestResults(document) + this.removeCachedDecorationTypes(document) + } + + removeCachedTestResults(document: vscode.TextDocument) { + if (!document || document.isUntitled) { + return + } + + const filePath = document.fileName + this.testResultProvider.removeCachedResults(filePath) + } + + removeCachedDecorationTypes(document: vscode.TextDocument) { + if (!document || !document.fileName) { + return + } + + delete this.failingAssertionDecorators[document.fileName] + } + + onDidChangeActiveTextEditor(editor: vscode.TextEditor) { + this.triggerUpdateActiveEditor(editor) + } + + /** + * This event is fired with the document not dirty when: + * - before the onDidSaveTextDocument event + * - the document was changed by an external editor + */ + onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { + if (event.document.isDirty) { + return + } + if (event.document.uri.scheme === 'git') { + return + } + + // Ignore a clean file with a change: + if (event.contentChanges.length > 0) { + return + } + + this.removeCachedTestResults(event.document) + + for (const editor of vscode.window.visibleTextEditors) { + if (editor.document === event.document) { + this.triggerUpdateActiveEditor(editor) + } + } + } + + toggleCoverageOverlay() { + this.coverageOverlay.toggleVisibility() + + // restart jest since coverage condition has changed + this.triggerUpdateSettings(this.pluginSettings) + } + + private detectedSnapshotErrors() { + if (!this.pluginSettings.enableSnapshotUpdateMessages) { + return + } + vscode.window + .showInformationMessage('Would you like to update your Snapshots?', { title: 'Replace them' }) + .then((response) => { + // No response == cancel + if (response) { + this.jestProcess.runJestWithUpdateForSnapshots(() => { + if (this.pluginSettings.restartJestOnSnapshotUpdate) { + this.jestProcessManager.stopJestProcess(this.jestProcess).then(() => { + this.startProcess() + }) + vscode.window.showInformationMessage('Updated Snapshots and restarted Jest.') + } else { + vscode.window.showInformationMessage('Updated Snapshots. It will show in your next test run.') + } + }) + } + }) + } + + private resetInlineErrorDecorators(editor: vscode.TextEditor) { + if (!this.failingAssertionDecorators[editor.document.fileName]) { + this.failingAssertionDecorators[editor.document.fileName] = [] + return + } + + if (isOpenInMultipleEditors(editor.document)) { + return + } + + this.failingAssertionDecorators[editor.document.fileName].forEach((element) => { + element.dispose() + }) + this.failingAssertionDecorators[editor.document.fileName] = [] + } + + private generateInlineErrorDecorator(fileName: string, test: TestResult) { + const errorMessage = test.terseMessage || test.shortMessage + const decorator = { + range: new vscode.Range(test.lineNumberOfError, 0, test.lineNumberOfError, 0), + } + + // We have to make a new style for each unique message, this is + // why we have to remove off of them beforehand + const style = decorations.failingAssertionStyle(errorMessage) + this.failingAssertionDecorators[fileName].push(style) + + return { style, decorator } + } + + private handleStdErr(error: Buffer) { + const message = error.toString() + if (this.shouldIgnoreOutput(message)) { + return + } + + if (isWatchNotSupported(message)) { + this.jestProcess.watchMode = WatchMode.WatchAll + } + + const noANSI = cleanAnsi(message) + if (/(snapshots? failed)|(snapshot test failed)/i.test(noANSI)) { + this.detectedSnapshotErrors() + } + + this.channel.appendLine(noANSI) + } + + private handleJestEditorSupportEvent(output: string) { + if (output.includes('onRunStart')) { + this.channel.clear() + this.updateStatusBar('running', 'Running tests', false) + } + if (output.includes('onRunComplete')) { + this.updateStatusBar('stopped', undefined, false) + this.parsingTestFile = false + } + + if (!this.shouldIgnoreOutput(output)) { + this.channel.appendLine(output) + } + } + + private assignHandlers(jestProcess: JestProcess) { + jestProcess + .onJestEditorSupportEvent('executableJSON', (data: JestTotalResults) => { + this.updateWithData(data) + }) + .onJestEditorSupportEvent('executableOutput', this.handleJestEditorSupportEvent) + .onJestEditorSupportEvent('executableStdErr', (error: Buffer) => this.handleStdErr(error)) + .onJestEditorSupportEvent('nonTerminalError', (error: string) => { + this.channel.appendLine(`Received an error from Jest Runner: ${error.toString()}`) + this.channel.show(true) + }) + .onJestEditorSupportEvent('exception', (result) => { + this.channel.appendLine(`\nException raised: [${result.type}]: ${result.message}\n`) + this.channel.show(true) + }) + .onJestEditorSupportEvent('terminalError', (error: string) => { + this.channel.appendLine('\nException raised: ' + error) + this.channel.show(true) + }) + } + + private setupStatusBar() { + this.updateStatusBar('initial', undefined, false) + } + + private updateStatusBar(status: Status, details?: string, watchMode: boolean = true) { + const modes: Mode[] = [] + if (this.coverageOverlay.enabled) { + modes.push('coverage') + } + if (watchMode) { + modes.push('watch') + } + this.status.update(status, details, modes) + } + + private setupDecorators() { + this.passingItStyle = decorations.passingItName() + this.failingItStyle = decorations.failingItName() + this.skipItStyle = decorations.skipItName() + this.unknownItStyle = decorations.notRanItName() + } + + private shouldIgnoreOutput(text: string): boolean { + // this fails when snapshots change - to be revised - returning always false for now + return text.includes('Watch Usage') || text.includes('onRunComplete') || text.includes('onRunStart') + } + + private updateWithData(data: JestTotalResults) { + const noAnsiData = resultsWithoutAnsiEscapeSequence(data) + const normalizedData = resultsWithLowerCaseWindowsDriveLetters(noAnsiData) + this.coverageMapProvider.update(normalizedData.coverageMap) + + const statusList = this.testResultProvider.updateTestResults(normalizedData) + updateDiagnostics(statusList, this.failDiagnostics) + + const failedFileCount = failedSuiteCount(this.failDiagnostics) + if (failedFileCount <= 0 && normalizedData.success) { + this.updateStatusBar('success') + } else { + this.updateStatusBar('failed', ` (${failedFileCount} test suite${failedFileCount > 1 ? 's' : ''} failed)`) + } + + for (const editor of vscode.window.visibleTextEditors) { + if (vscode.workspace.getWorkspaceFolder(editor.document.uri) === this.workspaceFolder) { + this.triggerUpdateActiveEditor(editor) + } + } + } + + private generateDotsForItBlocks(blocks: TestResult[], state: TestReconciliationState): DecorationOptions[] { + const nameForState = { + [TestReconciliationState.KnownSuccess]: 'Passed', + [TestReconciliationState.KnownFail]: 'Failed', + [TestReconciliationState.KnownSkip]: 'Skipped', + [TestReconciliationState.Unknown]: 'Test has not run yet, due to Jest only running tests related to changes.', + } + + return blocks.map((it) => { + return { + range: new vscode.Range(it.start.line, it.start.column, it.start.line, it.start.column + 1), + hoverMessage: nameForState[state], + identifier: it.name, + } + }) + } +} diff --git a/src/JestProcessManagement/JestProcess.ts b/src/JestProcessManagement/JestProcess.ts index 250c9c12f..53b2f129f 100644 --- a/src/JestProcessManagement/JestProcess.ts +++ b/src/JestProcessManagement/JestProcess.ts @@ -46,7 +46,7 @@ export class JestProcess { } public stop(): Promise<void> { - return new Promise(resolve => { + return new Promise((resolve) => { this.keepAliveCounter = 1 this.stopResolveCallback = resolve this.jestSupportEvents.clear() diff --git a/src/JestProcessManagement/JestProcessManager.ts b/src/JestProcessManagement/JestProcessManager.ts index 20b3f0ee8..2844e3419 100644 --- a/src/JestProcessManagement/JestProcessManager.ts +++ b/src/JestProcessManagement/JestProcessManager.ts @@ -30,7 +30,7 @@ export class JestProcessManager { keepAlive?: boolean } = {}): JestProcess { if (watchMode !== WatchMode.None && this.runAllTestsFirstInWatchMode) { - return this.runAllTestsFirst(exitedJestProcess => { + return this.runAllTestsFirst((exitedJestProcess) => { // cancel the rest execution if stop() has been requested. if (exitedJestProcess.stopRequested()) { return @@ -55,7 +55,7 @@ export class JestProcessManager { public stopAll() { const processesToRemove = [...this.jestProcesses] this.jestProcesses = [] - return Promise.all(processesToRemove.map(jestProcess => jestProcess.stop())) + return Promise.all(processesToRemove.map((jestProcess) => jestProcess.stop())) } public stopJestProcess(jestProcess: JestProcess) { diff --git a/src/Settings/index.ts b/src/Settings/index.ts index 17de6184f..07a63a41b 100644 --- a/src/Settings/index.ts +++ b/src/Settings/index.ts @@ -1,32 +1,32 @@ -import { TestState } from '../DebugCodeLens' - -export interface IPluginResourceSettings { - autoEnable?: boolean - enableInlineErrorMessages?: boolean - enableSnapshotUpdateMessages?: boolean - pathToConfig?: string - pathToJest?: string - restartJestOnSnapshotUpdate?: boolean - rootPath?: string - runAllTestsFirst?: boolean - showCoverageOnLoad: boolean - coverageFormatter: string - debugMode?: boolean -} - -export interface IPluginWindowSettings { - debugCodeLens: { - enabled: boolean - showWhenTestStateIn: TestState[] - } - enableSnapshotPreviews?: boolean - disabledWorkspaceFolders: string[] -} - -export function isDefaultPathToJest(str) { - return str === null || str === '' -} - -export function hasUserSetPathToJest(str) { - return !isDefaultPathToJest(str) -} +import { TestState } from '../DebugCodeLens' + +export interface IPluginResourceSettings { + autoEnable?: boolean + enableInlineErrorMessages?: boolean + enableSnapshotUpdateMessages?: boolean + pathToConfig?: string + pathToJest?: string + restartJestOnSnapshotUpdate?: boolean + rootPath?: string + runAllTestsFirst?: boolean + showCoverageOnLoad: boolean + coverageFormatter: string + debugMode?: boolean +} + +export interface IPluginWindowSettings { + debugCodeLens: { + enabled: boolean + showWhenTestStateIn: TestState[] + } + enableSnapshotPreviews?: boolean + disabledWorkspaceFolders: string[] +} + +export function isDefaultPathToJest(str) { + return str === null || str === '' +} + +export function hasUserSetPathToJest(str) { + return !isDefaultPathToJest(str) +} diff --git a/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts b/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts index ab42f445a..0b6887bb1 100644 --- a/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts +++ b/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts @@ -1,44 +1,44 @@ -import * as vscode from 'vscode' -import { Snapshot } from 'jest-editor-support' - -import { extensionName } from '../appGlobals' -import { previewCommand } from './SnapshotPreviewProvider' - -const missingSnapshotCommand = `${extensionName}.snapshot.missing` - -export function registerSnapshotCodeLens(enableSnapshotPreviews: boolean) { - if (!enableSnapshotPreviews) { - return [] - } - return [ - vscode.languages.registerCodeLensProvider({ pattern: '**/*.{ts,tsx,js,jsx}' }, new SnapshotCodeLensProvider()), - vscode.commands.registerCommand(missingSnapshotCommand, () => { - vscode.window.showInformationMessage('Run test to generate snapshot.') - }), - ] -} - -class SnapshotCodeLensProvider implements vscode.CodeLensProvider { - public provideCodeLenses(document: vscode.TextDocument, _token: vscode.CancellationToken) { - const snapshots = new Snapshot() - return snapshots.getMetadata(document.uri.fsPath).map(snapshot => { - const { line } = snapshot.node.loc.start - const range = new vscode.Range(line - 1, 0, line - 1, 0) - let command: vscode.Command - if (snapshot.exists) { - command = { - title: 'view snapshot', - command: previewCommand, - arguments: [snapshot], - } - } else { - command = { - title: 'snapshot missing', - command: missingSnapshotCommand, - } - } - - return new vscode.CodeLens(range, command) - }) - } -} +import * as vscode from 'vscode' +import { Snapshot } from 'jest-editor-support' + +import { extensionName } from '../appGlobals' +import { previewCommand } from './SnapshotPreviewProvider' + +const missingSnapshotCommand = `${extensionName}.snapshot.missing` + +export function registerSnapshotCodeLens(enableSnapshotPreviews: boolean) { + if (!enableSnapshotPreviews) { + return [] + } + return [ + vscode.languages.registerCodeLensProvider({ pattern: '**/*.{ts,tsx,js,jsx}' }, new SnapshotCodeLensProvider()), + vscode.commands.registerCommand(missingSnapshotCommand, () => { + vscode.window.showInformationMessage('Run test to generate snapshot.') + }), + ] +} + +class SnapshotCodeLensProvider implements vscode.CodeLensProvider { + public provideCodeLenses(document: vscode.TextDocument, _token: vscode.CancellationToken) { + const snapshots = new Snapshot() + return snapshots.getMetadata(document.uri.fsPath).map((snapshot) => { + const { line } = snapshot.node.loc.start + const range = new vscode.Range(line - 1, 0, line - 1, 0) + let command: vscode.Command + if (snapshot.exists) { + command = { + title: 'view snapshot', + command: previewCommand, + arguments: [snapshot], + } + } else { + command = { + title: 'snapshot missing', + command: missingSnapshotCommand, + } + } + + return new vscode.CodeLens(range, command) + }) + } +} diff --git a/src/SnapshotCodeLens/SnapshotPreviewProvider.ts b/src/SnapshotCodeLens/SnapshotPreviewProvider.ts index 2f458678a..303545100 100644 --- a/src/SnapshotCodeLens/SnapshotPreviewProvider.ts +++ b/src/SnapshotCodeLens/SnapshotPreviewProvider.ts @@ -1,40 +1,40 @@ -import * as vscode from 'vscode' -import { SnapshotMetadata } from 'jest-editor-support' - -import { extensionName } from '../appGlobals' - -export const previewCommand = `${extensionName}.snapshot.preview` - -export function registerSnapshotPreview() { - let panel: vscode.WebviewPanel = null - - const escaped = (snapshot: string) => { - if (snapshot) { - // tslint:disable-next-line no-shadowed-variable - const escaped = snapshot - .replace(/&/g, '&') - .replace(/"/g, '"') - .replace(/'/g, ''') - .replace(/</g, '<') - .replace(/>/g, '>') - return `<pre>${escaped}</pre>` - } - } - - return [ - vscode.commands.registerCommand(previewCommand, (snapshot: SnapshotMetadata) => { - if (panel) { - panel.reveal() - } else { - panel = vscode.window.createWebviewPanel('view_snapshot', snapshot.name, vscode.ViewColumn.Two, {}) - - panel.onDidDispose(() => { - panel = null - }) - } - - panel.webview.html = escaped(snapshot.content) - panel.title = snapshot.name - }), - ] -} +import * as vscode from 'vscode' +import { SnapshotMetadata } from 'jest-editor-support' + +import { extensionName } from '../appGlobals' + +export const previewCommand = `${extensionName}.snapshot.preview` + +export function registerSnapshotPreview() { + let panel: vscode.WebviewPanel = null + + const escaped = (snapshot: string) => { + if (snapshot) { + // tslint:disable-next-line no-shadowed-variable + const escaped = snapshot + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(/</g, '<') + .replace(/>/g, '>') + return `<pre>${escaped}</pre>` + } + } + + return [ + vscode.commands.registerCommand(previewCommand, (snapshot: SnapshotMetadata) => { + if (panel) { + panel.reveal() + } else { + panel = vscode.window.createWebviewPanel('view_snapshot', snapshot.name, vscode.ViewColumn.Two, {}) + + panel.onDidDispose(() => { + panel = null + }) + } + + panel.webview.html = escaped(snapshot.content) + panel.title = snapshot.name + }), + ] +} diff --git a/src/SnapshotCodeLens/index.ts b/src/SnapshotCodeLens/index.ts index 2b1873c09..285892efb 100644 --- a/src/SnapshotCodeLens/index.ts +++ b/src/SnapshotCodeLens/index.ts @@ -1,2 +1,2 @@ -export { registerSnapshotCodeLens } from './SnapshotCodeLensProvider' -export { registerSnapshotPreview } from './SnapshotPreviewProvider' +export { registerSnapshotCodeLens } from './SnapshotCodeLensProvider' +export { registerSnapshotPreview } from './SnapshotPreviewProvider' diff --git a/src/StatusBar.ts b/src/StatusBar.ts index b5e657601..32fd02cf1 100644 --- a/src/StatusBar.ts +++ b/src/StatusBar.ts @@ -1,257 +1,257 @@ -import * as vscode from 'vscode' -import { extensionName } from './appGlobals' -import { JestExt } from './JestExt' - -export enum StatusType { - active, - summary, -} - -export type Status = 'running' | 'failed' | 'success' | 'stopped' | 'initial' -export type Mode = 'watch' | 'coverage' - -interface StatusUpdateRequest { - source: string - status: Status - details?: string - modes?: Mode[] -} - -interface SpinnableStatusBarItem extends Pick<vscode.StatusBarItem, 'command' | 'text' | 'tooltip'> { - readonly type: StatusType - show(): void - hide(): void -} - -const createStatusBarItem = (type: StatusType, priority: number): SpinnableStatusBarItem => { - const item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, priority) - return { - type, - show: () => item.show(), - hide: () => item.hide(), - - get command() { - return item.command - }, - get text() { - return item.text - }, - get tooltip() { - return item.tooltip - }, - - set command(_command) { - item.command = _command - }, - set text(_text: string) { - item.text = _text - }, - set tooltip(_tooltip: string) { - item.tooltip = _tooltip - }, - } -} - -// The bottom status bar -export class StatusBar { - private activeStatusItem = createStatusBarItem(StatusType.active, 2) - private summaryStatusItem = createStatusBarItem(StatusType.summary, 1) - - private priorities: Status[] = ['running', 'failed', 'success', 'stopped', 'initial'] - private requests = new Map<string, StatusUpdateRequest>() - private _activeFolder?: string - private summaryOutput?: vscode.OutputChannel - - constructor() { - this.summaryStatusItem.tooltip = 'Jest status summary of the workspace' - this.activeStatusItem.tooltip = 'Jest status of the active folder' - } - - register(getExtension: (name: string) => JestExt | undefined) { - const showSummaryOutput = `${extensionName}.show-summary-output` - const showActiveOutput = `${extensionName}.show-active-output` - this.summaryStatusItem.command = showSummaryOutput - this.activeStatusItem.command = showActiveOutput - - return [ - vscode.commands.registerCommand(showSummaryOutput, () => { - if (this.summaryOutput) { - this.summaryOutput.show() - } - }), - vscode.commands.registerCommand(showActiveOutput, () => { - if (this.activeFolder) { - const ext = getExtension(this.activeFolder) - if (ext) { - ext.channel.show() - } - } - }), - ] - } - bind(source: string) { - return { - update: (status: Status, details?: string, modes?: Mode[]) => { - this.request(source, status, details, modes) - }, - } - } - - onDidChangeActiveTextEditor(editor: vscode.TextEditor) { - if (editor && editor.document) { - const folder = vscode.workspace.getWorkspaceFolder(editor.document.uri) - if (folder && folder.name !== this._activeFolder) { - this._activeFolder = folder.name - this.updateActiveStatus() - } - } - } - - private request(source: string, status: Status, details?: string, modes?: Mode[]) { - const request: StatusUpdateRequest = { - source, - status, - details, - modes, - } - this.requests.set(source, request) - this.updateStatus(request) - } - private updateStatus(request: StatusUpdateRequest) { - this.updateActiveStatus(request) - this.updateSummaryStatus() - } - - private get activeFolder() { - if (!this._activeFolder) { - if (vscode.workspace.workspaceFolders.length === 1) { - // there's only one workspaceFolder, so let's take it - this._activeFolder = vscode.workspace.workspaceFolders[0].name - } else if (vscode.window.activeTextEditor) { - // otherwise select correct workspaceFolder based on the currently open textEditor - const folder = vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri) - if (folder) { - this._activeFolder = folder.name - } - } - } - return this._activeFolder - } - - private updateActiveStatus(request?: StatusUpdateRequest) { - if (request && this.activeFolder) { - if (request.source === this.activeFolder) { - this.render(request, this.activeStatusItem) - } - return - } - - // find the active item from requests - let _request = null - if (this.activeFolder) { - _request = this.requests.get(this.activeFolder) - } - if (!_request && this.requests.size === 1) { - _request = this.requests.values().next().value - } - - if (_request) { - this.render(_request, this.activeStatusItem) - } else { - this.activeStatusItem.hide() - } - } - - private updateSummaryStatus() { - if (this.needsSummaryStatus()) { - this.updateSummaryOutput() - - let summaryStatus: StatusUpdateRequest | undefined - let prev = 99 - for (const r of this.requests.values()) { - const idx = this.priorities.indexOf(r.status) - if (idx >= 0 && idx < prev) { - summaryStatus = r - prev = idx - } - } - - if (summaryStatus) { - this.render(summaryStatus, this.summaryStatusItem) - return - } - } - this.summaryStatusItem.hide() - } - - private render(request: StatusUpdateRequest, statusBarItem: SpinnableStatusBarItem) { - const message = this.getMessageByStatus(request.status) - - switch (statusBarItem.type) { - case StatusType.active: - const modes = this.getModes(request.modes) - const details = !this.needsSummaryStatus() && request.details ? request.details : '' - const displayString = [message, details, modes].filter(s => s && s.length > 0).join(' ') - statusBarItem.text = `Jest: ${displayString}` - statusBarItem.tooltip = `Jest status of '${this.activeFolder}'` - break - case StatusType.summary: - statusBarItem.text = `Jest-WS: ${message}` - break - default: - throw new Error(`unexpected statusType: ${statusBarItem.type}`) - } - statusBarItem.show() - } - - private updateSummaryOutput() { - if (!this.summaryOutput) { - this.summaryOutput = vscode.window.createOutputChannel('Jest (Workspace)') - } - this.summaryOutput.clear() - - const messages = [] - this.requests.forEach(item => { - const details = item.details ? `: ${item.details}` : '' - messages.push(`${item.source}: ${item.status} ${details}`) - }) - this.summaryOutput.append(messages.join('\n')) - } - - private needsSummaryStatus() { - return this.requests.size > 1 - } - - private getMessageByStatus(status: Status) { - switch (status) { - case 'running': - return '$(sync~spin)' - case 'failed': - return '$(alert)' - case 'success': - return '$(check)' - case 'initial': - return '...' - default: - return status - } - } - private getModes(modes?: Mode[]) { - if (!modes) { - return '' - } - const modesStrings = modes.map(m => { - switch (m) { - case 'coverage': - return '$(color-mode)' - case 'watch': - return '$(eye)' - default: - throw new Error(`unrecognized mode: ${m}`) - } - }) - return modesStrings.join(' ') - } -} - -export const statusBar = new StatusBar() +import * as vscode from 'vscode' +import { extensionName } from './appGlobals' +import { JestExt } from './JestExt' + +export enum StatusType { + active, + summary, +} + +export type Status = 'running' | 'failed' | 'success' | 'stopped' | 'initial' +export type Mode = 'watch' | 'coverage' + +interface StatusUpdateRequest { + source: string + status: Status + details?: string + modes?: Mode[] +} + +interface SpinnableStatusBarItem extends Pick<vscode.StatusBarItem, 'command' | 'text' | 'tooltip'> { + readonly type: StatusType + show(): void + hide(): void +} + +const createStatusBarItem = (type: StatusType, priority: number): SpinnableStatusBarItem => { + const item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, priority) + return { + type, + show: () => item.show(), + hide: () => item.hide(), + + get command() { + return item.command + }, + get text() { + return item.text + }, + get tooltip() { + return item.tooltip + }, + + set command(_command) { + item.command = _command + }, + set text(_text: string) { + item.text = _text + }, + set tooltip(_tooltip: string) { + item.tooltip = _tooltip + }, + } +} + +// The bottom status bar +export class StatusBar { + private activeStatusItem = createStatusBarItem(StatusType.active, 2) + private summaryStatusItem = createStatusBarItem(StatusType.summary, 1) + + private priorities: Status[] = ['running', 'failed', 'success', 'stopped', 'initial'] + private requests = new Map<string, StatusUpdateRequest>() + private _activeFolder?: string + private summaryOutput?: vscode.OutputChannel + + constructor() { + this.summaryStatusItem.tooltip = 'Jest status summary of the workspace' + this.activeStatusItem.tooltip = 'Jest status of the active folder' + } + + register(getExtension: (name: string) => JestExt | undefined) { + const showSummaryOutput = `${extensionName}.show-summary-output` + const showActiveOutput = `${extensionName}.show-active-output` + this.summaryStatusItem.command = showSummaryOutput + this.activeStatusItem.command = showActiveOutput + + return [ + vscode.commands.registerCommand(showSummaryOutput, () => { + if (this.summaryOutput) { + this.summaryOutput.show() + } + }), + vscode.commands.registerCommand(showActiveOutput, () => { + if (this.activeFolder) { + const ext = getExtension(this.activeFolder) + if (ext) { + ext.channel.show() + } + } + }), + ] + } + bind(source: string) { + return { + update: (status: Status, details?: string, modes?: Mode[]) => { + this.request(source, status, details, modes) + }, + } + } + + onDidChangeActiveTextEditor(editor: vscode.TextEditor) { + if (editor && editor.document) { + const folder = vscode.workspace.getWorkspaceFolder(editor.document.uri) + if (folder && folder.name !== this._activeFolder) { + this._activeFolder = folder.name + this.updateActiveStatus() + } + } + } + + private request(source: string, status: Status, details?: string, modes?: Mode[]) { + const request: StatusUpdateRequest = { + source, + status, + details, + modes, + } + this.requests.set(source, request) + this.updateStatus(request) + } + private updateStatus(request: StatusUpdateRequest) { + this.updateActiveStatus(request) + this.updateSummaryStatus() + } + + private get activeFolder() { + if (!this._activeFolder) { + if (vscode.workspace.workspaceFolders.length === 1) { + // there's only one workspaceFolder, so let's take it + this._activeFolder = vscode.workspace.workspaceFolders[0].name + } else if (vscode.window.activeTextEditor) { + // otherwise select correct workspaceFolder based on the currently open textEditor + const folder = vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri) + if (folder) { + this._activeFolder = folder.name + } + } + } + return this._activeFolder + } + + private updateActiveStatus(request?: StatusUpdateRequest) { + if (request && this.activeFolder) { + if (request.source === this.activeFolder) { + this.render(request, this.activeStatusItem) + } + return + } + + // find the active item from requests + let _request = null + if (this.activeFolder) { + _request = this.requests.get(this.activeFolder) + } + if (!_request && this.requests.size === 1) { + _request = this.requests.values().next().value + } + + if (_request) { + this.render(_request, this.activeStatusItem) + } else { + this.activeStatusItem.hide() + } + } + + private updateSummaryStatus() { + if (this.needsSummaryStatus()) { + this.updateSummaryOutput() + + let summaryStatus: StatusUpdateRequest | undefined + let prev = 99 + for (const r of this.requests.values()) { + const idx = this.priorities.indexOf(r.status) + if (idx >= 0 && idx < prev) { + summaryStatus = r + prev = idx + } + } + + if (summaryStatus) { + this.render(summaryStatus, this.summaryStatusItem) + return + } + } + this.summaryStatusItem.hide() + } + + private render(request: StatusUpdateRequest, statusBarItem: SpinnableStatusBarItem) { + const message = this.getMessageByStatus(request.status) + + switch (statusBarItem.type) { + case StatusType.active: + const modes = this.getModes(request.modes) + const details = !this.needsSummaryStatus() && request.details ? request.details : '' + const displayString = [message, details, modes].filter((s) => s && s.length > 0).join(' ') + statusBarItem.text = `Jest: ${displayString}` + statusBarItem.tooltip = `Jest status of '${this.activeFolder}'` + break + case StatusType.summary: + statusBarItem.text = `Jest-WS: ${message}` + break + default: + throw new Error(`unexpected statusType: ${statusBarItem.type}`) + } + statusBarItem.show() + } + + private updateSummaryOutput() { + if (!this.summaryOutput) { + this.summaryOutput = vscode.window.createOutputChannel('Jest (Workspace)') + } + this.summaryOutput.clear() + + const messages = [] + this.requests.forEach((item) => { + const details = item.details ? `: ${item.details}` : '' + messages.push(`${item.source}: ${item.status} ${details}`) + }) + this.summaryOutput.append(messages.join('\n')) + } + + private needsSummaryStatus() { + return this.requests.size > 1 + } + + private getMessageByStatus(status: Status) { + switch (status) { + case 'running': + return '$(sync~spin)' + case 'failed': + return '$(alert)' + case 'success': + return '$(check)' + case 'initial': + return '...' + default: + return status + } + } + private getModes(modes?: Mode[]) { + if (!modes) { + return '' + } + const modesStrings = modes.map((m) => { + switch (m) { + case 'coverage': + return '$(color-mode)' + case 'watch': + return '$(eye)' + default: + throw new Error(`unrecognized mode: ${m}`) + } + }) + return modesStrings.join(' ') + } +} + +export const statusBar = new StatusBar() diff --git a/src/TestResults/TestReconciliationState.ts b/src/TestResults/TestReconciliationState.ts index 54093fcc3..02a58804d 100644 --- a/src/TestResults/TestReconciliationState.ts +++ b/src/TestResults/TestReconciliationState.ts @@ -1,9 +1,9 @@ -export type TestReconciliationState = 'Unknown' | 'KnownSuccess' | 'KnownFail' | 'KnownSkip' - -// tslint:disable-next-line variable-name -export const TestReconciliationState = { - Unknown: 'Unknown' as TestReconciliationState, - KnownSuccess: 'KnownSuccess' as TestReconciliationState, - KnownFail: 'KnownFail' as TestReconciliationState, - KnownSkip: 'KnownSkip' as TestReconciliationState, -} +export type TestReconciliationState = 'Unknown' | 'KnownSuccess' | 'KnownFail' | 'KnownSkip' + +// tslint:disable-next-line variable-name +export const TestReconciliationState = { + Unknown: 'Unknown' as TestReconciliationState, + KnownSuccess: 'KnownSuccess' as TestReconciliationState, + KnownFail: 'KnownFail' as TestReconciliationState, + KnownSkip: 'KnownSkip' as TestReconciliationState, +} diff --git a/src/TestResults/TestResult.ts b/src/TestResults/TestResult.ts index 9d4eff935..4522dcd6b 100644 --- a/src/TestResults/TestResult.ts +++ b/src/TestResults/TestResult.ts @@ -1,120 +1,120 @@ -import { TestReconciliationState } from './TestReconciliationState' -import { JestFileResults, JestTotalResults } from 'jest-editor-support' -import { FileCoverage } from 'istanbul-lib-coverage' -import * as path from 'path' -import { cleanAnsi } from '../helpers' - -interface Position { - /** Zero-based column number */ - column: number - - /** Zero-based line number */ - line: number -} - -export interface TestResult { - name: string - start: Position - end: Position - - status: TestReconciliationState - shortMessage?: string - terseMessage?: string - - /** Zero-based line number */ - lineNumberOfError?: number -} - -/** - * Normalize file paths on Windows systems to use lowercase drive letters. - * This follows the standard used by Visual Studio Code for URIs which includes - * the document fileName property. - * - * @param data Parsed JSON results - */ -export function resultsWithLowerCaseWindowsDriveLetters(data: JestTotalResults) { - if (path.sep === '\\') { - return { - ...data, - coverageMap: coverageMapWithLowerCaseWindowsDriveLetters(data), - testResults: testResultsWithLowerCaseWindowsDriveLetters(data.testResults), - } - } - - return data -} - -/** - * Removes ANSI escape sequence characters from test results in order to get clean messages - */ -export function resultsWithoutAnsiEscapeSequence(data: JestTotalResults) { - if (!data || !data.testResults) { - return data - } - - return { - ...data, - testResults: data.testResults.map(result => ({ - ...result, - message: cleanAnsi(result.message), - assertionResults: result.assertionResults.map(assertion => ({ - ...assertion, - failureMessages: assertion.failureMessages.map(message => cleanAnsi(message)), - })), - })), - } -} - -export function coverageMapWithLowerCaseWindowsDriveLetters(data: JestTotalResults) { - if (!data.coverageMap) { - return - } - - const result = {} - const filePaths = Object.keys(data.coverageMap) - for (const filePath of filePaths) { - const newFileCoverage = fileCoverageWithLowerCaseWindowsDriveLetter(data.coverageMap[filePath]) - result[newFileCoverage.path] = newFileCoverage - } - - return result -} - -function fileCoverageWithLowerCaseWindowsDriveLetter(fileCoverage: FileCoverage) { - const newFilePath = withLowerCaseWindowsDriveLetter(fileCoverage.path) - if (newFilePath) { - return { - ...fileCoverage, - path: newFilePath, - } - } - - return fileCoverage -} - -export function testResultsWithLowerCaseWindowsDriveLetters(testResults: JestFileResults[]): JestFileResults[] { - if (!testResults) { - return testResults - } - - return testResults.map(testResultWithLowerCaseWindowsDriveLetter) -} - -function testResultWithLowerCaseWindowsDriveLetter(testResult: JestFileResults): JestFileResults { - const newFilePath = withLowerCaseWindowsDriveLetter(testResult.name) - if (newFilePath) { - return { - ...testResult, - name: newFilePath, - } - } - - return testResult -} - -export function withLowerCaseWindowsDriveLetter(filePath: string): string | undefined { - const match = filePath.match(/^([A-Z]:\\)(.*)$/) - if (match) { - return `${match[1].toLowerCase()}${match[2]}` - } -} +import { TestReconciliationState } from './TestReconciliationState' +import { JestFileResults, JestTotalResults } from 'jest-editor-support' +import { FileCoverage } from 'istanbul-lib-coverage' +import * as path from 'path' +import { cleanAnsi } from '../helpers' + +interface Position { + /** Zero-based column number */ + column: number + + /** Zero-based line number */ + line: number +} + +export interface TestResult { + name: string + start: Position + end: Position + + status: TestReconciliationState + shortMessage?: string + terseMessage?: string + + /** Zero-based line number */ + lineNumberOfError?: number +} + +/** + * Normalize file paths on Windows systems to use lowercase drive letters. + * This follows the standard used by Visual Studio Code for URIs which includes + * the document fileName property. + * + * @param data Parsed JSON results + */ +export function resultsWithLowerCaseWindowsDriveLetters(data: JestTotalResults) { + if (path.sep === '\\') { + return { + ...data, + coverageMap: coverageMapWithLowerCaseWindowsDriveLetters(data), + testResults: testResultsWithLowerCaseWindowsDriveLetters(data.testResults), + } + } + + return data +} + +/** + * Removes ANSI escape sequence characters from test results in order to get clean messages + */ +export function resultsWithoutAnsiEscapeSequence(data: JestTotalResults) { + if (!data || !data.testResults) { + return data + } + + return { + ...data, + testResults: data.testResults.map((result) => ({ + ...result, + message: cleanAnsi(result.message), + assertionResults: result.assertionResults.map((assertion) => ({ + ...assertion, + failureMessages: assertion.failureMessages.map((message) => cleanAnsi(message)), + })), + })), + } +} + +export function coverageMapWithLowerCaseWindowsDriveLetters(data: JestTotalResults) { + if (!data.coverageMap) { + return + } + + const result = {} + const filePaths = Object.keys(data.coverageMap) + for (const filePath of filePaths) { + const newFileCoverage = fileCoverageWithLowerCaseWindowsDriveLetter(data.coverageMap[filePath]) + result[newFileCoverage.path] = newFileCoverage + } + + return result +} + +function fileCoverageWithLowerCaseWindowsDriveLetter(fileCoverage: FileCoverage) { + const newFilePath = withLowerCaseWindowsDriveLetter(fileCoverage.path) + if (newFilePath) { + return { + ...fileCoverage, + path: newFilePath, + } + } + + return fileCoverage +} + +export function testResultsWithLowerCaseWindowsDriveLetters(testResults: JestFileResults[]): JestFileResults[] { + if (!testResults) { + return testResults + } + + return testResults.map(testResultWithLowerCaseWindowsDriveLetter) +} + +function testResultWithLowerCaseWindowsDriveLetter(testResult: JestFileResults): JestFileResults { + const newFilePath = withLowerCaseWindowsDriveLetter(testResult.name) + if (newFilePath) { + return { + ...testResult, + name: newFilePath, + } + } + + return testResult +} + +export function withLowerCaseWindowsDriveLetter(filePath: string): string | undefined { + const match = filePath.match(/^([A-Z]:\\)(.*)$/) + if (match) { + return `${match[1].toLowerCase()}${match[2]}` + } +} diff --git a/src/TestResults/TestResultProvider.ts b/src/TestResults/TestResultProvider.ts index ceaf75681..da6aca1b0 100644 --- a/src/TestResults/TestResultProvider.ts +++ b/src/TestResults/TestResultProvider.ts @@ -78,8 +78,8 @@ export class TestResultProvider { const remainingTests: ItBlock[] = [] const _trackRemaining = trackRemaining === undefined ? true : trackRemaining - _itBlocks.forEach(test => { - const matched = remainingAssertions.filter(a => _isMatched.every(m => m(test, a))) + _itBlocks.forEach((test) => { + const matched = remainingAssertions.filter((a) => _isMatched.every((m) => m(test, a))) if (matched.length === 1) { const aIndex = remainingAssertions.indexOf(matched[0]) if (aIndex < 0) { @@ -123,7 +123,7 @@ export class TestResultProvider { return false } const parts = t.name.split(templateLiteralPattern) - const r = parts.every(p => a.title.includes(p)) + const r = parts.every((p) => a.title.includes(p)) return r } const onMatchError: OnMatchError = (t: ItBlock, match: TestAssertionStatus[]) => { @@ -163,7 +163,7 @@ export class TestResultProvider { } // convert remaining itBlocks to unmatched result - itBlocks.forEach(t => totalResult.push(toMatchResult(t))) + itBlocks.forEach((t) => totalResult.push(toMatchResult(t))) this.resultsByFilePath[filePath] = totalResult return totalResult diff --git a/src/TestResults/index.ts b/src/TestResults/index.ts index 3df085e67..685de54a7 100644 --- a/src/TestResults/index.ts +++ b/src/TestResults/index.ts @@ -1,3 +1,3 @@ -export * from './TestReconciliationState' -export { TestResult, resultsWithLowerCaseWindowsDriveLetters } from './TestResult' -export * from './TestResultProvider' +export * from './TestReconciliationState' +export { TestResult, resultsWithLowerCaseWindowsDriveLetters } from './TestResult' +export * from './TestResultProvider' diff --git a/src/appGlobals.ts b/src/appGlobals.ts index ffb8cfc23..72fd3eff0 100644 --- a/src/appGlobals.ts +++ b/src/appGlobals.ts @@ -1,2 +1,2 @@ -export const extensionName = 'io.orta.jest' -export const extensionId = 'orta.vscode-jest' +export const extensionName = 'io.orta.jest' +export const extensionId = 'orta.vscode-jest' diff --git a/src/diagnostics.ts b/src/diagnostics.ts index e4266b053..96441063d 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -40,7 +40,7 @@ export function updateCurrentDiagnostics( diagnostics.set( uri, - testResult.map(r => { + testResult.map((r) => { const line = r.lineNumberOfError || r.end.line const textLine = editor.document.lineAt(line) return createDiagnosticWithRange(r.shortMessage, textLine.range) @@ -61,14 +61,14 @@ export function updateDiagnostics(testResults: TestFileAssertionStatus[], diagno } function addTestsError(result: TestFileAssertionStatus, uri: vscode.Uri) { - const asserts = result.assertions.filter(a => a.status === TestReconciliationState.KnownFail) + const asserts = result.assertions.filter((a) => a.status === TestReconciliationState.KnownFail) diagnostics.set( uri, - asserts.map(assertion => createDiagnostic(assertion.shortMessage || assertion.message, assertion.line)) + asserts.map((assertion) => createDiagnostic(assertion.shortMessage || assertion.message, assertion.line)) ) } - testResults.forEach(result => { + testResults.forEach((result) => { const uri = vscode.Uri.file(result.file) switch (result.status) { case TestReconciliationState.KnownFail: @@ -86,12 +86,12 @@ export function updateDiagnostics(testResults: TestFileAssertionStatus[], diagno // Remove diagnostics for files no longer in existence const toBeDeleted = [] - diagnostics.forEach(uri => { + diagnostics.forEach((uri) => { if (!existsSync(uri.fsPath)) { toBeDeleted.push(uri) } }) - toBeDeleted.forEach(uri => { + toBeDeleted.forEach((uri) => { diagnostics.delete(uri) }) } diff --git a/src/extension.ts b/src/extension.ts index c6beb6f38..a3872af4d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -21,15 +21,15 @@ export function activate(context: vscode.ExtensionContext) { context.subscriptions.push( ...statusBar.register((folder: string) => extensionManager.getByName(folder)), - extensionManager.registerCommand(`${extensionName}.start`, extension => { + extensionManager.registerCommand(`${extensionName}.start`, (extension) => { vscode.window.showInformationMessage('Started Jest, press escape to hide this message.') extension.startProcess() }), - extensionManager.registerCommand(`${extensionName}.stop`, extension => extension.stopProcess()), - extensionManager.registerCommand(`${extensionName}.restart`, extension => extension.restartProcess()), + extensionManager.registerCommand(`${extensionName}.stop`, (extension) => extension.stopProcess()), + extensionManager.registerCommand(`${extensionName}.restart`, (extension) => extension.restartProcess()), // dublicate of "show-output" maybe remove? - extensionManager.registerCommand(`${extensionName}.show-channel`, extension => extension.channel.show()), - extensionManager.registerCommand(`${extensionName}.coverage.toggle`, extension => + extensionManager.registerCommand(`${extensionName}.show-channel`, (extension) => extension.channel.show()), + extensionManager.registerCommand(`${extensionName}.coverage.toggle`, (extension) => extension.toggleCoverageOverlay() ), vscode.commands.registerCommand( @@ -43,7 +43,7 @@ export function activate(context: vscode.ExtensionContext) { ...registerSnapshotPreview(), vscode.languages.registerCodeLensProvider( { pattern: '**/*.{ts,tsx,js,jsx}' }, - new CoverageCodeLensProvider(uri => extensionManager.getByDocUri(uri)) + new CoverageCodeLensProvider((uri) => extensionManager.getByDocUri(uri)) ), vscode.languages.registerCodeLensProvider(languages, extensionManager.debugCodeLensProvider), // this provides the opportunity to inject test names into the DebugConfiguration diff --git a/src/extensionManager.ts b/src/extensionManager.ts index e8d82f709..076c4a12e 100644 --- a/src/extensionManager.ts +++ b/src/extensionManager.ts @@ -24,7 +24,7 @@ export class ExtensionManager { this.commonPluginSettings = getExtensionWindowSettings() this.debugConfigurationProvider = new DebugConfigurationProvider() - this.debugCodeLensProvider = new DebugCodeLensProvider(uri => this.getByDocUri(uri)) + this.debugCodeLensProvider = new DebugCodeLensProvider((uri) => this.getByDocUri(uri)) this.applySettings(getExtensionWindowSettings()) this.registerAll() } @@ -142,7 +142,7 @@ export class ExtensionManager { this.applySettings(getExtensionWindowSettings()) this.registerAll() } - vscode.workspace.workspaceFolders.forEach(workspaceFolder => { + vscode.workspace.workspaceFolders.forEach((workspaceFolder) => { const jestExt = this.getByName(workspaceFolder.name) if (jestExt && e.affectsConfiguration('jest', workspaceFolder.uri)) { const updatedSettings = getExtensionResourceSettings(workspaceFolder.uri) diff --git a/src/messaging.ts b/src/messaging.ts index 536d7a8f9..0478939ee 100644 --- a/src/messaging.ts +++ b/src/messaging.ts @@ -28,7 +28,7 @@ export const TROUBLESHOOTING_URL = 'https://github.com/jest-community/vscode-jes // internal methods // function _extractActionTitles(actions?: MessageAction[]): string[] { - return actions ? actions.map(a => a.title) : [] + return actions ? actions.map((a) => a.title) : [] } // expose the internal function so we can unit testing it export function _handleMessageActions(actions?: MessageAction[]): (action?: string) => void { @@ -36,7 +36,7 @@ export function _handleMessageActions(actions?: MessageAction[]): (action?: stri if (!action) { return } - const found = actions.filter(a => a.title === action) + const found = actions.filter((a) => a.title === action) if (found.length === 1) { found[0].action() } else { diff --git a/src/types.ts b/src/types.ts index 5348228f3..83c2f61aa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ -import * as vscode from 'vscode' - -export interface DecorationOptions extends vscode.DecorationOptions { - identifier: string -} +import * as vscode from 'vscode' + +export interface DecorationOptions extends vscode.DecorationOptions { + identifier: string +} diff --git a/tests/Coverage/CoverageMapProvider.test.ts b/tests/Coverage/CoverageMapProvider.test.ts index a97fa33f7..46294b1d7 100644 --- a/tests/Coverage/CoverageMapProvider.test.ts +++ b/tests/Coverage/CoverageMapProvider.test.ts @@ -17,9 +17,9 @@ describe('CoverageMapProvider', () => { describe('map', () => { it('should return the coverage map', () => { - ;(createCoverageMap as jest.Mock<any>).mockImplementation(map => map) + ;(createCoverageMap as jest.Mock<any>).mockImplementation((map) => map) createSourceMapStore.mockReturnValueOnce({ - transformCoverage: map => ({ map }), + transformCoverage: (map) => ({ map }), }) const expected: any = {} @@ -40,8 +40,8 @@ describe('CoverageMapProvider', () => { describe('update()', () => { it('should transform the coverage map', () => { const expected: any = {} - ;(createCoverageMap as jest.Mock<any>).mockImplementation(map => map) - const transformCoverage = jest.fn().mockImplementationOnce(map => ({ map })) + ;(createCoverageMap as jest.Mock<any>).mockImplementation((map) => map) + const transformCoverage = jest.fn().mockImplementationOnce((map) => ({ map })) createSourceMapStore.mockReturnValueOnce({ transformCoverage }) const sut = new CoverageMapProvider() @@ -72,7 +72,7 @@ describe('CoverageMapProvider', () => { merge: mergeFn, }) createSourceMapStore.mockReturnValue({ - transformCoverage: m => ({ map: m }), + transformCoverage: (m) => ({ map: m }), }) const sut = new CoverageMapProvider() diff --git a/tests/Coverage/Formatters/DefaultFormatter.test.ts b/tests/Coverage/Formatters/DefaultFormatter.test.ts index d4fe7a4e0..d8ee3d3f0 100644 --- a/tests/Coverage/Formatters/DefaultFormatter.test.ts +++ b/tests/Coverage/Formatters/DefaultFormatter.test.ts @@ -234,7 +234,10 @@ describe('DefaultFormatter', () => { sut.formatUncoveredLines(editor, fileCoverage) expect(vscode.Range).toHaveBeenCalledTimes(2) - expect((vscode.Range as jest.Mock<vscode.Range>).mock.calls).toEqual([[0, 0, 0, 0], [9, 0, 9, 0]]) + expect((vscode.Range as jest.Mock<vscode.Range>).mock.calls).toEqual([ + [0, 0, 0, 0], + [9, 0, 9, 0], + ]) }) it('should add decorations with the reindexed ranges', () => { diff --git a/tests/DebugCodeLens/DebugCodeLensProvider.test.ts b/tests/DebugCodeLens/DebugCodeLensProvider.test.ts index 6b684846a..4f08a7336 100644 --- a/tests/DebugCodeLens/DebugCodeLensProvider.test.ts +++ b/tests/DebugCodeLens/DebugCodeLensProvider.test.ts @@ -160,7 +160,7 @@ describe('DebugCodeLensProvider', () => { }) it('should not show the CodeLens above failing tests unless configured', () => { - const testStates = allTestStates.filter(s => s !== TestState.Fail) + const testStates = allTestStates.filter((s) => s !== TestState.Fail) const status = TestReconciliationState.KnownFail const sut = new DebugCodeLensProvider(provideJestExt, testStates) getResults.mockReturnValueOnce([{ status }]) @@ -169,7 +169,7 @@ describe('DebugCodeLensProvider', () => { }) it('should not show the CodeLens above passing tests unless configured', () => { - const testStates = allTestStates.filter(s => s !== TestState.Pass) + const testStates = allTestStates.filter((s) => s !== TestState.Pass) const status = TestReconciliationState.KnownSuccess const sut = new DebugCodeLensProvider(provideJestExt, testStates) getResults.mockReturnValueOnce([{ status }]) @@ -177,7 +177,7 @@ describe('DebugCodeLensProvider', () => { expect(sut.provideCodeLenses(document, token)).toEqual([]) }) it('should not show the CodeLens above skipped tests unless configured', () => { - const testStates = allTestStates.filter(s => s !== TestState.Skip) + const testStates = allTestStates.filter((s) => s !== TestState.Skip) const status = TestReconciliationState.KnownSkip const sut = new DebugCodeLensProvider(provideJestExt, testStates) getResults.mockReturnValueOnce([{ status }]) @@ -185,7 +185,7 @@ describe('DebugCodeLensProvider', () => { expect(sut.provideCodeLenses(document, token)).toEqual([]) }) it('should not show the CodeLens above unknown tests unless configured', () => { - const testStates = allTestStates.filter(s => s !== TestState.Unknown) + const testStates = allTestStates.filter((s) => s !== TestState.Unknown) const status = TestReconciliationState.Unknown const sut = new DebugCodeLensProvider(provideJestExt, testStates) getResults.mockReturnValueOnce([{ status }]) diff --git a/tests/Jest/index.test.ts b/tests/Jest/index.test.ts index bf60cf89e..f7e896640 100644 --- a/tests/Jest/index.test.ts +++ b/tests/Jest/index.test.ts @@ -6,7 +6,7 @@ describe('isWatchNotSupported', () => { const str = '\n--watch is not supported without git/hg, please use --watchAll \n' expect(isWatchNotSupported(str)).toBe(true) }) - + it('returns true when matching an "out of the repository" message', () => { const str = ` Determining test suites to run... diff --git a/tests/JestExt.test.ts b/tests/JestExt.test.ts index 402c13a7a..3b919b38d 100644 --- a/tests/JestExt.test.ts +++ b/tests/JestExt.test.ts @@ -1,799 +1,799 @@ -jest.unmock('events') -jest.unmock('../src/JestExt') -jest.mock('../src/helpers', () => ({ - cleanAnsi: (str: string) => str, - pathToJest: jest.fn(), - pathToConfig: jest.fn(), -})) - -jest.mock('../src/DebugCodeLens', () => ({ - DebugCodeLensProvider: class MockCodeLensProvider {}, -})) -jest.mock('os') -jest.mock('../src/decorations') - -const update = jest.fn() -const statusBar = { - bind: () => ({ update }), -} -jest.mock('../src/StatusBar', () => ({ statusBar })) - -import { JestExt } from '../src/JestExt' -import { ProjectWorkspace } from 'jest-editor-support' -import { window, workspace, debug } from 'vscode' -import { hasDocument, isOpenInMultipleEditors } from '../src/editor' -import * as decorations from '../src/decorations' -import { updateCurrentDiagnostics } from '../src/diagnostics' -import { JestProcessManager, JestProcess } from '../src/JestProcessManagement' -import * as messaging from '../src/messaging' - -describe('JestExt', () => { - const getConfiguration = workspace.getConfiguration as jest.Mock<any> - const workspaceFolder = { name: 'test-folder' } as any - let projectWorkspace: ProjectWorkspace - const channelStub = { appendLine: jest.fn(), clear: jest.fn(), show: jest.fn() } as any - const extensionSettings = { debugCodeLens: {} } as any - const debugCodeLensProvider = {} as any - const debugConfigurationProvider = { - provideDebugConfigurations: jest.fn(), - prepareTestRun: jest.fn(), - } as any - - // tslint:disable-next-line no-console - console.error = jest.fn() - // tslint:disable-next-line no-console - console.warn = jest.fn() - - beforeEach(() => { - jest.resetAllMocks() - - projectWorkspace = new ProjectWorkspace(null, null, null, null) - getConfiguration.mockReturnValue({}) - }) - - describe('resetInlineErrorDecorators()', () => { - let sut: JestExt - const editor: any = { - document: { fileName: 'file.js' }, - setDecorations: jest.fn(), - } - const decorationType: any = { dispose: jest.fn() } - - beforeEach(() => { - sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - - sut.canUpdateActiveEditor = jest.fn().mockReturnValueOnce(true) - sut.debugCodeLensProvider.didChange = jest.fn() - ;((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValue({}) - ;((sut.testResultProvider.getSortedResults as unknown) as jest.Mock<{}>).mockReturnValueOnce({ - success: [], - fail: [], - skip: [], - unknown: [], - }) - }) - - it('should initialize the cached decoration types as an empty array', () => { - expect(sut.failingAssertionDecorators[editor.document.fileName]).toBeUndefined() - sut.triggerUpdateActiveEditor(editor) - - expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([]) - expect(isOpenInMultipleEditors).not.toBeCalled() - }) - - it('should not clear the cached decorations types when the document is open more than once', () => { - ;((isOpenInMultipleEditors as unknown) as jest.Mock<{}>).mockReturnValueOnce(true) - - sut.failingAssertionDecorators[editor.document.fileName] = { - forEach: jest.fn(), - } as any - sut.triggerUpdateActiveEditor(editor) - - expect(sut.failingAssertionDecorators[editor.document.fileName].forEach).not.toBeCalled() - }) - - it('should dispose of each cached decoration type', () => { - sut.failingAssertionDecorators[editor.document.fileName] = [decorationType] - sut.triggerUpdateActiveEditor(editor) - - expect(decorationType.dispose).toBeCalled() - }) - - it('should reset the cached decoration types', () => { - sut.failingAssertionDecorators[editor.document.fileName] = [decorationType] - sut.triggerUpdateActiveEditor(editor) - - expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([]) - }) - }) - - describe('generateInlineErrorDecorator()', () => { - it('should add the decoration type to the cache', () => { - const settings: any = { - debugCodeLens: {}, - enableInlineErrorMessages: true, - } - const sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - settings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - const editor: any = { - document: { fileName: 'file.js' }, - setDecorations: jest.fn(), - } - const expected = {} - ;((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValueOnce(expected) - sut.canUpdateActiveEditor = jest.fn().mockReturnValueOnce(true) - sut.testResultProvider.getSortedResults = jest.fn().mockReturnValueOnce({ - success: [], - fail: [ - { - start: {}, - }, - ], - skip: [], - unknown: [], - }) - sut.debugCodeLensProvider.didChange = jest.fn() - sut.triggerUpdateActiveEditor(editor) - - expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([expected]) - }) - }) - - // tslint:disable no-shadowed-variable - describe('runTest()', () => { - const workspaceFolder = {} as any - const fileName = 'fileName' - const testNamePattern = 'testNamePattern' - - it('should run the supplied test', async () => { - const startDebugging = (debug.startDebugging as unknown) as jest.Mock<{}> - ;((startDebugging as unknown) as jest.Mock<{}>).mockImplementation(async (_folder: any, nameOrConfig: any) => { - // trigger fallback to default configuration - if (typeof nameOrConfig === 'string') { - throw null - } - }) - - const debugConfiguration = { type: 'dummyconfig' } - const sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - ;((sut.debugConfigurationProvider.provideDebugConfigurations as unknown) as jest.Mock<{}>).mockReturnValue([ - debugConfiguration, - ]) - - await sut.runTest(workspaceFolder, fileName, testNamePattern) - - expect(debug.startDebugging).toHaveBeenCalledWith(workspaceFolder, debugConfiguration) - - const configuration = startDebugging.mock.calls[startDebugging.mock.calls.length - 1][1] - expect(configuration).toBeDefined() - expect(configuration.type).toBe('dummyconfig') - - expect(sut.debugConfigurationProvider.prepareTestRun).toBeCalledWith(fileName, testNamePattern) - }) - }) - - describe('onDidCloseTextDocument()', () => { - const projectWorkspace = new ProjectWorkspace(null, null, null, null) - const sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - const document = {} as any - sut.removeCachedTestResults = jest.fn() - sut.removeCachedDecorationTypes = jest.fn() - - it('should remove the cached test results', () => { - sut.onDidCloseTextDocument(document) - expect(sut.removeCachedTestResults).toBeCalledWith(document) - }) - - it('should remove the cached decorations', () => { - sut.onDidCloseTextDocument(document) - expect(sut.removeCachedDecorationTypes) - }) - }) - - describe('removeCachedTestResults()', () => { - const projectWorkspace = new ProjectWorkspace(null, null, null, null) - const sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - sut.testResultProvider.removeCachedResults = jest.fn() - - it('should do nothing when the document is falsy', () => { - sut.removeCachedTestResults(null) - expect(sut.testResultProvider.removeCachedResults).not.toBeCalled() - }) - - it('should do nothing when the document is untitled', () => { - const document: any = { isUntitled: true } as any - sut.removeCachedTestResults(document) - - expect(sut.testResultProvider.removeCachedResults).not.toBeCalled() - }) - - it('should reset the test result cache for the document', () => { - const expected = 'file.js' - sut.removeCachedTestResults({ fileName: expected } as any) - - expect(sut.testResultProvider.removeCachedResults).toBeCalledWith(expected) - }) - }) - - describe('removeCachedAnnotations()', () => { - const projectWorkspace = new ProjectWorkspace(null, null, null, null) - const sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - - beforeEach(() => { - sut.failingAssertionDecorators = { - 'file.js': [], - } - }) - - it('should do nothing when the document is falsy', () => { - sut.onDidCloseTextDocument(null) - - expect(sut.failingAssertionDecorators['file.js']).toBeDefined() - }) - - it('should remove the annotations for the document', () => { - const document: any = { fileName: 'file.js' } as any - sut.onDidCloseTextDocument(document) - - expect(sut.failingAssertionDecorators['file.js']).toBeUndefined() - }) - }) - - describe('onDidChangeActiveTextEditor()', () => { - let sut - const editor: any = {} - const projectWorkspace = new ProjectWorkspace(null, null, null, null) - sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - sut.triggerUpdateActiveEditor = jest.fn() - - beforeEach(() => { - ;(sut.triggerUpdateActiveEditor as jest.Mock<{}>).mockReset() - }) - - it('should update the annotations when the editor has a document', () => { - ;((hasDocument as unknown) as jest.Mock<{}>).mockReturnValueOnce(true) - sut.onDidChangeActiveTextEditor(editor) - - expect(sut.triggerUpdateActiveEditor).toBeCalledWith(editor) - }) - }) - - describe('onDidChangeTextDocument()', () => { - let sut - const event: any = { - document: { - isDirty: false, - uri: { scheme: 'file' }, - }, - contentChanges: [], - } - - beforeEach(() => { - const projectWorkspace = new ProjectWorkspace(null, null, null, null) - sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - }) - - function expectItTakesNoAction(event) { - sut.removeCachedTestResults = jest.fn() - sut.triggerUpdateActiveEditor = jest.fn() - sut.onDidChangeTextDocument(event) - - expect(sut.removeCachedTestResults).not.toBeCalledWith(event.document) - expect(sut.triggerUpdateActiveEditor).not.toBeCalled() - } - - it('should do nothing if the document has unsaved changes', () => { - const event: any = { - document: { - isDirty: true, - uri: { scheme: 'file' }, - }, - contentChanges: [], - } - expectItTakesNoAction(event) - }) - - it('should do nothing if the document URI scheme is "git"', () => { - const event: any = { - document: { - isDirty: false, - uri: { - scheme: 'git', - }, - }, - contentChanges: [], - } - expectItTakesNoAction(event) - }) - - it('should do nothing if the document is clean but there are changes', () => { - const event = { - document: { - isDirty: false, - uri: { scheme: 'file' }, - }, - contentChanges: { length: 1 }, - } - expectItTakesNoAction(event) - }) - - it('should remove the cached test results if the document is clean', () => { - sut.removeCachedTestResults = jest.fn() - window.visibleTextEditors = [] - sut.onDidChangeTextDocument(event) - - expect(sut.removeCachedTestResults).toBeCalledWith(event.document) - }) - - it('should update the decorations', () => { - const editor: any = { document: event.document } - sut.triggerUpdateActiveEditor = jest.fn() - window.visibleTextEditors = [editor] - sut.onDidChangeTextDocument(event) - - expect(sut.triggerUpdateActiveEditor).toBeCalledWith(editor) - }) - }) - - describe('toggleCoverageOverlay()', () => { - it('should toggle the coverage overlay visibility', () => { - const sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - sut.triggerUpdateSettings = jest.fn() - sut.toggleCoverageOverlay() - - expect(sut.coverageOverlay.toggleVisibility).toBeCalled() - expect(sut.triggerUpdateSettings).toBeCalled() - }) - it('overrides showCoverageOnLoad settings', () => { - const settings = { showCoverageOnLoad: true } as any - const sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - settings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - expect(projectWorkspace.collectCoverage).toBe(true) - - sut.restartProcess = jest.fn() - sut.coverageOverlay.enabled = false - sut.toggleCoverageOverlay() - - expect(projectWorkspace.collectCoverage).toBe(false) - }) - }) - - describe('triggerUpdateActiveEditor()', () => { - beforeEach(() => { - jest.resetAllMocks() - }) - it('should update the coverage overlay in visible editors', () => { - const editor: any = {} - - const sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - sut.triggerUpdateActiveEditor(editor) - - expect(sut.coverageOverlay.updateVisibleEditors).toBeCalled() - }) - it('should update both decorators and diagnostics for valid editor', () => { - const sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - sut.updateDecorators = jest.fn() - const mockEditor: any = { - document: { uri: { fsPath: 'file://a/b/c.ts' } }, - } - ;((sut.testResultProvider.getSortedResults as unknown) as jest.Mock<{}>).mockReturnValueOnce({ - success: [], - fail: [], - skip: [], - unknown: [], - }) - sut.triggerUpdateActiveEditor(mockEditor) - - expect(sut.updateDecorators).toBeCalled() - expect(updateCurrentDiagnostics).toBeCalled() - }) - }) - - describe('canUpdateActiveEditor', () => { - const mockTextEditor = (ext: string): any => { - const extension = ext.length ? `.${ext}` : '' - return { - document: { uri: { fsPath: `file://a/b/c${extension}` } }, - } - } - - let sut - beforeEach(() => { - jest.resetAllMocks() - const projectWorkspace = new ProjectWorkspace(null, null, null, null) - sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - extensionSettings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - }) - it('will skip if there is no document in editor', () => { - const editor: any = {} - expect(sut.canUpdateActiveEditor(editor)).toBe(false) - }) - it('can not update if file is being parsed', () => { - expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(true) - sut.parsingTestFile = true - expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(false) - }) - it('can only update if document is a typescript or javascript file', () => { - expect(sut.canUpdateActiveEditor(mockTextEditor('json'))).toBe(false) - expect(sut.canUpdateActiveEditor(mockTextEditor(''))).toBe(false) - - expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(true) - expect(sut.canUpdateActiveEditor(mockTextEditor('jsx'))).toBe(true) - expect(sut.canUpdateActiveEditor(mockTextEditor('ts'))).toBe(true) - expect(sut.canUpdateActiveEditor(mockTextEditor('tsx'))).toBe(true) - }) - }) - describe('updateDecorators', () => { - let sut: JestExt - const mockEditor: any = { document: { uri: { fsPath: `file://a/b/c.js` } } } - const emptyTestResults = { success: [], fail: [], skip: [], unknown: [] } - - const settings: any = { - debugCodeLens: {}, - enableInlineErrorMessages: false, - } - - const tr1 = { - start: { line: 1, column: 0 }, - } - const tr2 = { - start: { line: 100, column: 0 }, - } - - beforeEach(() => { - jest.resetAllMocks() - ;((decorations.failingItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'fail' }) - ;((decorations.passingItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'pass' }) - ;((decorations.skipItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'skip' }) - ;((decorations.notRanItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'notRan' }) - - const projectWorkspace = new ProjectWorkspace(null, null, null, null) - sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - settings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - - mockEditor.setDecorations = jest.fn() - sut.debugCodeLensProvider.didChange = jest.fn() - }) - - it('will reset decorator if testResults is empty', () => { - sut.updateDecorators(emptyTestResults, mockEditor) - expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4) - for (const args of mockEditor.setDecorations.mock.calls) { - expect(args[1].length).toBe(0) - } - }) - it('will generate dot dectorations for test results', () => { - const testResults2: any = { success: [tr1], fail: [tr2], skip: [], unknown: [] } - sut.updateDecorators(testResults2, mockEditor) - expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4) - for (const args of mockEditor.setDecorations.mock.calls) { - switch (args[0].key) { - case 'fail': - case 'pass': - expect(args[1].length).toBe(1) - break - case 'skip': - case 'notRan': - expect(args[1].length).toBe(0) - break - default: - expect(args[0].key).toBe('never be here') - } - } - }) - - it('will update inlineError decorator only if setting is enabled', () => { - const testResults2: any = { success: [], fail: [tr1, tr2], skip: [], unknown: [] } - const expected = {} - ;((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValueOnce(expected) - sut.updateDecorators(testResults2, mockEditor) - expect(decorations.failingAssertionStyle).not.toBeCalled() - expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4) - - jest.clearAllMocks() - settings.enableInlineErrorMessages = true - sut.updateDecorators(testResults2, mockEditor) - expect(decorations.failingAssertionStyle).toHaveBeenCalledTimes(2) - expect(mockEditor.setDecorations).toHaveBeenCalledTimes(6) - }) - }) - - describe('detectedSnapshotErrors()', () => { - let sut: JestExt - const mockEditor: any = { document: { uri: { fsPath: `file://a/b/c.js` } } } - - const settings: any = { - debugCodeLens: {}, - enableSnapshotUpdateMessages: true, - } - - beforeEach(() => { - jest.resetAllMocks() - const projectWorkspace = new ProjectWorkspace(null, null, null, null) - sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - settings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - - mockEditor.setDecorations = jest.fn() - sut.debugCodeLensProvider.didChange = jest.fn() - }) - - it('will trigger snapshot update message when a snapshot test fails', () => { - window.showInformationMessage = jest.fn(async () => null) - const spy = jest.spyOn(sut as any, 'detectedSnapshotErrors') - ;(sut as any).handleStdErr(new Error('Snapshot test failed')) - ;(sut as any).handleStdErr(new Error('Snapshot failed')) - ;(sut as any).handleStdErr(new Error('Snapshots failed')) - ;(sut as any).handleStdErr(new Error('Failed for some other reason')) - expect(spy).toHaveBeenCalledTimes(3) - }) - }) - - describe('startProcess', () => { - const projectWorkspace = new ProjectWorkspace(null, null, null, null) - - const mockJestProcess = () => { - const mockProcess: any = new JestProcess({ projectWorkspace }) - mockProcess.onJestEditorSupportEvent.mockReturnValue(mockProcess) - return mockProcess - } - const createJestExt = (settings: any, instanceSettings = { multirootEnv: false }) => { - ;(JestProcessManager as jest.Mock).mockClear() - const mockProcess: any = mockJestProcess() - - const jestExt = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - settings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - instanceSettings - ) - const mockProcessManager: any = (JestProcessManager as jest.Mock).mock.instances[0] - mockProcessManager.startJestProcess.mockReturnValue(mockProcess) - return [jestExt, mockProcessManager] - } - it('if process already running, do nothing', () => { - const [sut, mockProcessManager] = createJestExt(extensionSettings) - mockProcessManager.numberOfProcesses = 1 - sut.startProcess() - expect(mockProcessManager.startJestProcess).not.toHaveBeenCalled() - }) - it('can start all tests first if configured.', () => { - const [sut, mockProcessManager] = createJestExt({ ...extensionSettings, runAllTestsFirst: true }) - - const { runAllTestsFirstInWatchMode } = (JestProcessManager as jest.Mock).mock.calls[0][0] - expect(runAllTestsFirstInWatchMode).toBeTruthy() - - sut.startProcess() - expect(mockProcessManager.startJestProcess).toHaveBeenCalled() - }) - it('can start watch mode first if configured.', () => { - const [sut, mockProcessManager] = createJestExt({ ...extensionSettings, runAllTestsFirst: false }) - - const { runAllTestsFirstInWatchMode } = (JestProcessManager as jest.Mock).mock.calls[0][0] - expect(runAllTestsFirstInWatchMode).toBeFalsy() - - sut.startProcess() - expect(mockProcessManager.startJestProcess).toHaveBeenCalled() - }) - - describe('exitCallback', () => { - const [sut, mockProcessManager] = createJestExt(extensionSettings, { multirootEnv: true }) - sut.startProcess() - const { exitCallback } = mockProcessManager.startJestProcess.mock.calls[0][0] - - it('if receive watchMode process: prepare and report for the next run', () => { - const p1: any = mockJestProcess() - const p2: any = mockJestProcess() - - exitCallback(p1, p2) - - expect(p1.onJestEditorSupportEvent).not.toHaveBeenCalled() - expect(p2.onJestEditorSupportEvent).toHaveBeenCalled() - }) - it('if process ends unexpectedly, report error', () => { - const p1: any = mockJestProcess() - p1.stopRequested.mockReturnValue(false) - exitCallback(p1) - expect(p1.onJestEditorSupportEvent).not.toHaveBeenCalled() - expect(messaging.systemErrorMessage).toHaveBeenCalled() - const msg: string = (messaging.systemErrorMessage as jest.Mock).mock.calls[0][0] - expect(msg.includes(workspaceFolder.name)).toBeTruthy() - }) - }) - }) - - describe('handleJestEditorSupportEvent()', () => { - let sut: JestExt - - const settings: any = { - debugCodeLens: {}, - enableSnapshotUpdateMessages: true, - } - - beforeEach(() => { - jest.resetAllMocks() - const projectWorkspace = new ProjectWorkspace(null, null, null, null) - sut = new JestExt( - null, - workspaceFolder, - projectWorkspace, - channelStub, - settings, - debugCodeLensProvider, - debugConfigurationProvider, - null, - null - ) - }) - - it('will change status according to received output', () => { - update.mockClear() - ;(sut as any).handleJestEditorSupportEvent('onRunStart') - expect(update).toHaveBeenCalledTimes(1) - expect(update).toHaveBeenCalledWith('running', 'Running tests', []) - update.mockClear() - ;(sut as any).handleJestEditorSupportEvent('onRunComplete') - expect(update).toHaveBeenCalledTimes(1) - expect(update).toHaveBeenCalledWith('stopped', undefined, []) - }) - - it('will append line to output', () => { - channelStub.appendLine.mockClear() - ;(sut as any).handleJestEditorSupportEvent('not ignored output') - expect(channelStub.appendLine).toHaveBeenCalledTimes(1) - expect(channelStub.appendLine).toHaveBeenCalledWith('not ignored output') - channelStub.appendLine.mockClear() - ;(sut as any).handleJestEditorSupportEvent('onRunComplete') - expect(channelStub.appendLine).not.toHaveBeenCalled() - }) - }) -}) +jest.unmock('events') +jest.unmock('../src/JestExt') +jest.mock('../src/helpers', () => ({ + cleanAnsi: (str: string) => str, + pathToJest: jest.fn(), + pathToConfig: jest.fn(), +})) + +jest.mock('../src/DebugCodeLens', () => ({ + DebugCodeLensProvider: class MockCodeLensProvider {}, +})) +jest.mock('os') +jest.mock('../src/decorations') + +const update = jest.fn() +const statusBar = { + bind: () => ({ update }), +} +jest.mock('../src/StatusBar', () => ({ statusBar })) + +import { JestExt } from '../src/JestExt' +import { ProjectWorkspace } from 'jest-editor-support' +import { window, workspace, debug } from 'vscode' +import { hasDocument, isOpenInMultipleEditors } from '../src/editor' +import * as decorations from '../src/decorations' +import { updateCurrentDiagnostics } from '../src/diagnostics' +import { JestProcessManager, JestProcess } from '../src/JestProcessManagement' +import * as messaging from '../src/messaging' + +describe('JestExt', () => { + const getConfiguration = workspace.getConfiguration as jest.Mock<any> + const workspaceFolder = { name: 'test-folder' } as any + let projectWorkspace: ProjectWorkspace + const channelStub = { appendLine: jest.fn(), clear: jest.fn(), show: jest.fn() } as any + const extensionSettings = { debugCodeLens: {} } as any + const debugCodeLensProvider = {} as any + const debugConfigurationProvider = { + provideDebugConfigurations: jest.fn(), + prepareTestRun: jest.fn(), + } as any + + // tslint:disable-next-line no-console + console.error = jest.fn() + // tslint:disable-next-line no-console + console.warn = jest.fn() + + beforeEach(() => { + jest.resetAllMocks() + + projectWorkspace = new ProjectWorkspace(null, null, null, null) + getConfiguration.mockReturnValue({}) + }) + + describe('resetInlineErrorDecorators()', () => { + let sut: JestExt + const editor: any = { + document: { fileName: 'file.js' }, + setDecorations: jest.fn(), + } + const decorationType: any = { dispose: jest.fn() } + + beforeEach(() => { + sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + + sut.canUpdateActiveEditor = jest.fn().mockReturnValueOnce(true) + sut.debugCodeLensProvider.didChange = jest.fn() + ;((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValue({}) + ;((sut.testResultProvider.getSortedResults as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + success: [], + fail: [], + skip: [], + unknown: [], + }) + }) + + it('should initialize the cached decoration types as an empty array', () => { + expect(sut.failingAssertionDecorators[editor.document.fileName]).toBeUndefined() + sut.triggerUpdateActiveEditor(editor) + + expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([]) + expect(isOpenInMultipleEditors).not.toBeCalled() + }) + + it('should not clear the cached decorations types when the document is open more than once', () => { + ;((isOpenInMultipleEditors as unknown) as jest.Mock<{}>).mockReturnValueOnce(true) + + sut.failingAssertionDecorators[editor.document.fileName] = { + forEach: jest.fn(), + } as any + sut.triggerUpdateActiveEditor(editor) + + expect(sut.failingAssertionDecorators[editor.document.fileName].forEach).not.toBeCalled() + }) + + it('should dispose of each cached decoration type', () => { + sut.failingAssertionDecorators[editor.document.fileName] = [decorationType] + sut.triggerUpdateActiveEditor(editor) + + expect(decorationType.dispose).toBeCalled() + }) + + it('should reset the cached decoration types', () => { + sut.failingAssertionDecorators[editor.document.fileName] = [decorationType] + sut.triggerUpdateActiveEditor(editor) + + expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([]) + }) + }) + + describe('generateInlineErrorDecorator()', () => { + it('should add the decoration type to the cache', () => { + const settings: any = { + debugCodeLens: {}, + enableInlineErrorMessages: true, + } + const sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + settings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + const editor: any = { + document: { fileName: 'file.js' }, + setDecorations: jest.fn(), + } + const expected = {} + ;((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValueOnce(expected) + sut.canUpdateActiveEditor = jest.fn().mockReturnValueOnce(true) + sut.testResultProvider.getSortedResults = jest.fn().mockReturnValueOnce({ + success: [], + fail: [ + { + start: {}, + }, + ], + skip: [], + unknown: [], + }) + sut.debugCodeLensProvider.didChange = jest.fn() + sut.triggerUpdateActiveEditor(editor) + + expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([expected]) + }) + }) + + // tslint:disable no-shadowed-variable + describe('runTest()', () => { + const workspaceFolder = {} as any + const fileName = 'fileName' + const testNamePattern = 'testNamePattern' + + it('should run the supplied test', async () => { + const startDebugging = (debug.startDebugging as unknown) as jest.Mock<{}> + ;((startDebugging as unknown) as jest.Mock<{}>).mockImplementation(async (_folder: any, nameOrConfig: any) => { + // trigger fallback to default configuration + if (typeof nameOrConfig === 'string') { + throw null + } + }) + + const debugConfiguration = { type: 'dummyconfig' } + const sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + ;((sut.debugConfigurationProvider.provideDebugConfigurations as unknown) as jest.Mock<{}>).mockReturnValue([ + debugConfiguration, + ]) + + await sut.runTest(workspaceFolder, fileName, testNamePattern) + + expect(debug.startDebugging).toHaveBeenCalledWith(workspaceFolder, debugConfiguration) + + const configuration = startDebugging.mock.calls[startDebugging.mock.calls.length - 1][1] + expect(configuration).toBeDefined() + expect(configuration.type).toBe('dummyconfig') + + expect(sut.debugConfigurationProvider.prepareTestRun).toBeCalledWith(fileName, testNamePattern) + }) + }) + + describe('onDidCloseTextDocument()', () => { + const projectWorkspace = new ProjectWorkspace(null, null, null, null) + const sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + const document = {} as any + sut.removeCachedTestResults = jest.fn() + sut.removeCachedDecorationTypes = jest.fn() + + it('should remove the cached test results', () => { + sut.onDidCloseTextDocument(document) + expect(sut.removeCachedTestResults).toBeCalledWith(document) + }) + + it('should remove the cached decorations', () => { + sut.onDidCloseTextDocument(document) + expect(sut.removeCachedDecorationTypes) + }) + }) + + describe('removeCachedTestResults()', () => { + const projectWorkspace = new ProjectWorkspace(null, null, null, null) + const sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + sut.testResultProvider.removeCachedResults = jest.fn() + + it('should do nothing when the document is falsy', () => { + sut.removeCachedTestResults(null) + expect(sut.testResultProvider.removeCachedResults).not.toBeCalled() + }) + + it('should do nothing when the document is untitled', () => { + const document: any = { isUntitled: true } as any + sut.removeCachedTestResults(document) + + expect(sut.testResultProvider.removeCachedResults).not.toBeCalled() + }) + + it('should reset the test result cache for the document', () => { + const expected = 'file.js' + sut.removeCachedTestResults({ fileName: expected } as any) + + expect(sut.testResultProvider.removeCachedResults).toBeCalledWith(expected) + }) + }) + + describe('removeCachedAnnotations()', () => { + const projectWorkspace = new ProjectWorkspace(null, null, null, null) + const sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + + beforeEach(() => { + sut.failingAssertionDecorators = { + 'file.js': [], + } + }) + + it('should do nothing when the document is falsy', () => { + sut.onDidCloseTextDocument(null) + + expect(sut.failingAssertionDecorators['file.js']).toBeDefined() + }) + + it('should remove the annotations for the document', () => { + const document: any = { fileName: 'file.js' } as any + sut.onDidCloseTextDocument(document) + + expect(sut.failingAssertionDecorators['file.js']).toBeUndefined() + }) + }) + + describe('onDidChangeActiveTextEditor()', () => { + let sut + const editor: any = {} + const projectWorkspace = new ProjectWorkspace(null, null, null, null) + sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + sut.triggerUpdateActiveEditor = jest.fn() + + beforeEach(() => { + ;(sut.triggerUpdateActiveEditor as jest.Mock<{}>).mockReset() + }) + + it('should update the annotations when the editor has a document', () => { + ;((hasDocument as unknown) as jest.Mock<{}>).mockReturnValueOnce(true) + sut.onDidChangeActiveTextEditor(editor) + + expect(sut.triggerUpdateActiveEditor).toBeCalledWith(editor) + }) + }) + + describe('onDidChangeTextDocument()', () => { + let sut + const event: any = { + document: { + isDirty: false, + uri: { scheme: 'file' }, + }, + contentChanges: [], + } + + beforeEach(() => { + const projectWorkspace = new ProjectWorkspace(null, null, null, null) + sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + }) + + function expectItTakesNoAction(event) { + sut.removeCachedTestResults = jest.fn() + sut.triggerUpdateActiveEditor = jest.fn() + sut.onDidChangeTextDocument(event) + + expect(sut.removeCachedTestResults).not.toBeCalledWith(event.document) + expect(sut.triggerUpdateActiveEditor).not.toBeCalled() + } + + it('should do nothing if the document has unsaved changes', () => { + const event: any = { + document: { + isDirty: true, + uri: { scheme: 'file' }, + }, + contentChanges: [], + } + expectItTakesNoAction(event) + }) + + it('should do nothing if the document URI scheme is "git"', () => { + const event: any = { + document: { + isDirty: false, + uri: { + scheme: 'git', + }, + }, + contentChanges: [], + } + expectItTakesNoAction(event) + }) + + it('should do nothing if the document is clean but there are changes', () => { + const event = { + document: { + isDirty: false, + uri: { scheme: 'file' }, + }, + contentChanges: { length: 1 }, + } + expectItTakesNoAction(event) + }) + + it('should remove the cached test results if the document is clean', () => { + sut.removeCachedTestResults = jest.fn() + window.visibleTextEditors = [] + sut.onDidChangeTextDocument(event) + + expect(sut.removeCachedTestResults).toBeCalledWith(event.document) + }) + + it('should update the decorations', () => { + const editor: any = { document: event.document } + sut.triggerUpdateActiveEditor = jest.fn() + window.visibleTextEditors = [editor] + sut.onDidChangeTextDocument(event) + + expect(sut.triggerUpdateActiveEditor).toBeCalledWith(editor) + }) + }) + + describe('toggleCoverageOverlay()', () => { + it('should toggle the coverage overlay visibility', () => { + const sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + sut.triggerUpdateSettings = jest.fn() + sut.toggleCoverageOverlay() + + expect(sut.coverageOverlay.toggleVisibility).toBeCalled() + expect(sut.triggerUpdateSettings).toBeCalled() + }) + it('overrides showCoverageOnLoad settings', () => { + const settings = { showCoverageOnLoad: true } as any + const sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + settings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + expect(projectWorkspace.collectCoverage).toBe(true) + + sut.restartProcess = jest.fn() + sut.coverageOverlay.enabled = false + sut.toggleCoverageOverlay() + + expect(projectWorkspace.collectCoverage).toBe(false) + }) + }) + + describe('triggerUpdateActiveEditor()', () => { + beforeEach(() => { + jest.resetAllMocks() + }) + it('should update the coverage overlay in visible editors', () => { + const editor: any = {} + + const sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + sut.triggerUpdateActiveEditor(editor) + + expect(sut.coverageOverlay.updateVisibleEditors).toBeCalled() + }) + it('should update both decorators and diagnostics for valid editor', () => { + const sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + sut.updateDecorators = jest.fn() + const mockEditor: any = { + document: { uri: { fsPath: 'file://a/b/c.ts' } }, + } + ;((sut.testResultProvider.getSortedResults as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + success: [], + fail: [], + skip: [], + unknown: [], + }) + sut.triggerUpdateActiveEditor(mockEditor) + + expect(sut.updateDecorators).toBeCalled() + expect(updateCurrentDiagnostics).toBeCalled() + }) + }) + + describe('canUpdateActiveEditor', () => { + const mockTextEditor = (ext: string): any => { + const extension = ext.length ? `.${ext}` : '' + return { + document: { uri: { fsPath: `file://a/b/c${extension}` } }, + } + } + + let sut + beforeEach(() => { + jest.resetAllMocks() + const projectWorkspace = new ProjectWorkspace(null, null, null, null) + sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + extensionSettings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + }) + it('will skip if there is no document in editor', () => { + const editor: any = {} + expect(sut.canUpdateActiveEditor(editor)).toBe(false) + }) + it('can not update if file is being parsed', () => { + expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(true) + sut.parsingTestFile = true + expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(false) + }) + it('can only update if document is a typescript or javascript file', () => { + expect(sut.canUpdateActiveEditor(mockTextEditor('json'))).toBe(false) + expect(sut.canUpdateActiveEditor(mockTextEditor(''))).toBe(false) + + expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(true) + expect(sut.canUpdateActiveEditor(mockTextEditor('jsx'))).toBe(true) + expect(sut.canUpdateActiveEditor(mockTextEditor('ts'))).toBe(true) + expect(sut.canUpdateActiveEditor(mockTextEditor('tsx'))).toBe(true) + }) + }) + describe('updateDecorators', () => { + let sut: JestExt + const mockEditor: any = { document: { uri: { fsPath: `file://a/b/c.js` } } } + const emptyTestResults = { success: [], fail: [], skip: [], unknown: [] } + + const settings: any = { + debugCodeLens: {}, + enableInlineErrorMessages: false, + } + + const tr1 = { + start: { line: 1, column: 0 }, + } + const tr2 = { + start: { line: 100, column: 0 }, + } + + beforeEach(() => { + jest.resetAllMocks() + ;((decorations.failingItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'fail' }) + ;((decorations.passingItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'pass' }) + ;((decorations.skipItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'skip' }) + ;((decorations.notRanItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'notRan' }) + + const projectWorkspace = new ProjectWorkspace(null, null, null, null) + sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + settings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + + mockEditor.setDecorations = jest.fn() + sut.debugCodeLensProvider.didChange = jest.fn() + }) + + it('will reset decorator if testResults is empty', () => { + sut.updateDecorators(emptyTestResults, mockEditor) + expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4) + for (const args of mockEditor.setDecorations.mock.calls) { + expect(args[1].length).toBe(0) + } + }) + it('will generate dot dectorations for test results', () => { + const testResults2: any = { success: [tr1], fail: [tr2], skip: [], unknown: [] } + sut.updateDecorators(testResults2, mockEditor) + expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4) + for (const args of mockEditor.setDecorations.mock.calls) { + switch (args[0].key) { + case 'fail': + case 'pass': + expect(args[1].length).toBe(1) + break + case 'skip': + case 'notRan': + expect(args[1].length).toBe(0) + break + default: + expect(args[0].key).toBe('never be here') + } + } + }) + + it('will update inlineError decorator only if setting is enabled', () => { + const testResults2: any = { success: [], fail: [tr1, tr2], skip: [], unknown: [] } + const expected = {} + ;((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValueOnce(expected) + sut.updateDecorators(testResults2, mockEditor) + expect(decorations.failingAssertionStyle).not.toBeCalled() + expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4) + + jest.clearAllMocks() + settings.enableInlineErrorMessages = true + sut.updateDecorators(testResults2, mockEditor) + expect(decorations.failingAssertionStyle).toHaveBeenCalledTimes(2) + expect(mockEditor.setDecorations).toHaveBeenCalledTimes(6) + }) + }) + + describe('detectedSnapshotErrors()', () => { + let sut: JestExt + const mockEditor: any = { document: { uri: { fsPath: `file://a/b/c.js` } } } + + const settings: any = { + debugCodeLens: {}, + enableSnapshotUpdateMessages: true, + } + + beforeEach(() => { + jest.resetAllMocks() + const projectWorkspace = new ProjectWorkspace(null, null, null, null) + sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + settings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + + mockEditor.setDecorations = jest.fn() + sut.debugCodeLensProvider.didChange = jest.fn() + }) + + it('will trigger snapshot update message when a snapshot test fails', () => { + window.showInformationMessage = jest.fn(async () => null) + const spy = jest.spyOn(sut as any, 'detectedSnapshotErrors') + ;(sut as any).handleStdErr(new Error('Snapshot test failed')) + ;(sut as any).handleStdErr(new Error('Snapshot failed')) + ;(sut as any).handleStdErr(new Error('Snapshots failed')) + ;(sut as any).handleStdErr(new Error('Failed for some other reason')) + expect(spy).toHaveBeenCalledTimes(3) + }) + }) + + describe('startProcess', () => { + const projectWorkspace = new ProjectWorkspace(null, null, null, null) + + const mockJestProcess = () => { + const mockProcess: any = new JestProcess({ projectWorkspace }) + mockProcess.onJestEditorSupportEvent.mockReturnValue(mockProcess) + return mockProcess + } + const createJestExt = (settings: any, instanceSettings = { multirootEnv: false }) => { + ;(JestProcessManager as jest.Mock).mockClear() + const mockProcess: any = mockJestProcess() + + const jestExt = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + settings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + instanceSettings + ) + const mockProcessManager: any = (JestProcessManager as jest.Mock).mock.instances[0] + mockProcessManager.startJestProcess.mockReturnValue(mockProcess) + return [jestExt, mockProcessManager] + } + it('if process already running, do nothing', () => { + const [sut, mockProcessManager] = createJestExt(extensionSettings) + mockProcessManager.numberOfProcesses = 1 + sut.startProcess() + expect(mockProcessManager.startJestProcess).not.toHaveBeenCalled() + }) + it('can start all tests first if configured.', () => { + const [sut, mockProcessManager] = createJestExt({ ...extensionSettings, runAllTestsFirst: true }) + + const { runAllTestsFirstInWatchMode } = (JestProcessManager as jest.Mock).mock.calls[0][0] + expect(runAllTestsFirstInWatchMode).toBeTruthy() + + sut.startProcess() + expect(mockProcessManager.startJestProcess).toHaveBeenCalled() + }) + it('can start watch mode first if configured.', () => { + const [sut, mockProcessManager] = createJestExt({ ...extensionSettings, runAllTestsFirst: false }) + + const { runAllTestsFirstInWatchMode } = (JestProcessManager as jest.Mock).mock.calls[0][0] + expect(runAllTestsFirstInWatchMode).toBeFalsy() + + sut.startProcess() + expect(mockProcessManager.startJestProcess).toHaveBeenCalled() + }) + + describe('exitCallback', () => { + const [sut, mockProcessManager] = createJestExt(extensionSettings, { multirootEnv: true }) + sut.startProcess() + const { exitCallback } = mockProcessManager.startJestProcess.mock.calls[0][0] + + it('if receive watchMode process: prepare and report for the next run', () => { + const p1: any = mockJestProcess() + const p2: any = mockJestProcess() + + exitCallback(p1, p2) + + expect(p1.onJestEditorSupportEvent).not.toHaveBeenCalled() + expect(p2.onJestEditorSupportEvent).toHaveBeenCalled() + }) + it('if process ends unexpectedly, report error', () => { + const p1: any = mockJestProcess() + p1.stopRequested.mockReturnValue(false) + exitCallback(p1) + expect(p1.onJestEditorSupportEvent).not.toHaveBeenCalled() + expect(messaging.systemErrorMessage).toHaveBeenCalled() + const msg: string = (messaging.systemErrorMessage as jest.Mock).mock.calls[0][0] + expect(msg.includes(workspaceFolder.name)).toBeTruthy() + }) + }) + }) + + describe('handleJestEditorSupportEvent()', () => { + let sut: JestExt + + const settings: any = { + debugCodeLens: {}, + enableSnapshotUpdateMessages: true, + } + + beforeEach(() => { + jest.resetAllMocks() + const projectWorkspace = new ProjectWorkspace(null, null, null, null) + sut = new JestExt( + null, + workspaceFolder, + projectWorkspace, + channelStub, + settings, + debugCodeLensProvider, + debugConfigurationProvider, + null, + null + ) + }) + + it('will change status according to received output', () => { + update.mockClear() + ;(sut as any).handleJestEditorSupportEvent('onRunStart') + expect(update).toHaveBeenCalledTimes(1) + expect(update).toHaveBeenCalledWith('running', 'Running tests', []) + update.mockClear() + ;(sut as any).handleJestEditorSupportEvent('onRunComplete') + expect(update).toHaveBeenCalledTimes(1) + expect(update).toHaveBeenCalledWith('stopped', undefined, []) + }) + + it('will append line to output', () => { + channelStub.appendLine.mockClear() + ;(sut as any).handleJestEditorSupportEvent('not ignored output') + expect(channelStub.appendLine).toHaveBeenCalledTimes(1) + expect(channelStub.appendLine).toHaveBeenCalledWith('not ignored output') + channelStub.appendLine.mockClear() + ;(sut as any).handleJestEditorSupportEvent('onRunComplete') + expect(channelStub.appendLine).not.toHaveBeenCalled() + }) + }) +}) diff --git a/tests/JestProcessManagement/JestProcess.test.ts b/tests/JestProcessManagement/JestProcess.test.ts index 49acba8a0..131c7fa20 100644 --- a/tests/JestProcessManagement/JestProcess.test.ts +++ b/tests/JestProcessManagement/JestProcess.test.ts @@ -298,7 +298,7 @@ describe('JestProcess', () => { keepAlive: true, }) jestProcess.onExit(onExit) - Array.from(Array(limit + 1).keys()).forEach(_ => { + Array.from(Array(limit + 1).keys()).forEach((_) => { eventEmitter.emit('debuggerProcessExit') }) expect(runnerMock.mock.instances.length).toBe(limit) diff --git a/tests/JestProcessManagement/JestProcessManager.test.ts b/tests/JestProcessManagement/JestProcessManager.test.ts index f5d149a52..f8d2edd5e 100644 --- a/tests/JestProcessManagement/JestProcessManager.test.ts +++ b/tests/JestProcessManagement/JestProcessManager.test.ts @@ -61,7 +61,7 @@ describe('JestProcessManager', () => { it('calls the onExit handler when JestProcess exits', () => { const mockImplementation = { keepAlive: false, - onExit: callback => { + onExit: (callback) => { eventEmitter.on('debuggerProcessExit', callback) }, } @@ -156,10 +156,10 @@ describe('JestProcessManager', () => { const eventEmitterForWatchMode = new EventEmitter() const onExitMock = jest .fn() - .mockImplementationOnce(callback => { + .mockImplementationOnce((callback) => { eventEmitter.on('debuggerProcessExit', callback) }) - .mockImplementationOnce(callback => { + .mockImplementationOnce((callback) => { eventEmitterForWatchMode.on('debuggerProcessExit', callback) }) @@ -184,10 +184,10 @@ describe('JestProcessManager', () => { const eventEmitterForWatchMode = new EventEmitter() const onExitMock = jest .fn() - .mockImplementationOnce(callback => { + .mockImplementationOnce((callback) => { eventEmitter.on('debuggerProcessExit', callback) }) - .mockImplementationOnce(callback => { + .mockImplementationOnce((callback) => { eventEmitterForWatchMode.on('debuggerProcessExit', callback) }) @@ -271,7 +271,7 @@ describe('JestProcessManager', () => { let isStopped = false const stop = () => (isStopped = true) const stopRequested = () => isStopped - const onExit = callback => { + const onExit = (callback) => { eventEmitter.on('debuggerProcessExit', callback) } return { onExit, stop, stopRequested } @@ -346,7 +346,7 @@ describe('JestProcessManager', () => { it('removes the reference to the jest process that has been stopped and the following onExit event does not do anything', () => { jestProcessMock.mockImplementation(() => ({ keepAlive: true, - onExit: callback => { + onExit: (callback) => { eventEmitter.on('debuggerProcessExit', callback) }, stop: jest.fn(), @@ -364,7 +364,7 @@ describe('JestProcessManager', () => { it('keeps the reference to the jest process that exited on its own but then restarted', () => { jestProcessMock.mockImplementation(() => ({ keepAlive: true, - onExit: callback => { + onExit: (callback) => { eventEmitter.on('debuggerProcessExit', callback) }, })) @@ -378,7 +378,7 @@ describe('JestProcessManager', () => { it('removes the reference to the jest process that exited on its own that preceeds the jest process for watch mode', () => { jestProcessMock.mockImplementation(() => ({ - onExit: callback => { + onExit: (callback) => { eventEmitter.on('debuggerProcessExit', callback) }, stopRequested: () => false, @@ -398,10 +398,10 @@ describe('JestProcessManager', () => { const eventEmitterForWatchMode = new EventEmitter() const onExitMock = jest .fn() - .mockImplementationOnce(callback => { + .mockImplementationOnce((callback) => { eventEmitter.on('debuggerProcessExit', callback) }) - .mockImplementationOnce(callback => { + .mockImplementationOnce((callback) => { eventEmitterForWatchMode.on('debuggerProcessExit', callback) }) @@ -435,7 +435,7 @@ describe('JestProcessManager', () => { it('removes the reference to the jest process that exited on its own', () => { jestProcessMock.mockImplementation(() => ({ keepAlive: false, - onExit: callback => { + onExit: (callback) => { eventEmitter.on('debuggerProcessExit', callback) }, restart: jest.fn(), @@ -452,10 +452,10 @@ describe('JestProcessManager', () => { const eventEmitterForWatchMode = new EventEmitter() const onExitMock = jest .fn() - .mockImplementationOnce(callback => { + .mockImplementationOnce((callback) => { eventEmitter.on('debuggerProcessExit', callback) }) - .mockImplementationOnce(callback => { + .mockImplementationOnce((callback) => { eventEmitterForWatchMode.on('debuggerProcessExit', callback) }) @@ -489,7 +489,7 @@ describe('JestProcessManager', () => { }) jestProcessMock.mockImplementation(() => ({ - onExit: callback => { + onExit: (callback) => { eventEmitter.on('debuggerProcessExit', callback) }, })) diff --git a/tests/TestResults/TestResultProvider.test.ts b/tests/TestResults/TestResultProvider.test.ts index 98e43ce9b..a310a1b34 100644 --- a/tests/TestResults/TestResultProvider.test.ts +++ b/tests/TestResults/TestResultProvider.test.ts @@ -206,7 +206,7 @@ describe('TestResultProvider', () => { const actual = sut.getResults(filePath) expect(actual).toHaveLength(2) - expect(actual.every(r => r.status === TestReconciliationState.KnownSuccess)).toEqual(true) + expect(actual.every((r) => r.status === TestReconciliationState.KnownSuccess)).toEqual(true) }) it('default to unknown if failed to match by line or location', () => { ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ @@ -227,8 +227,8 @@ describe('TestResultProvider', () => { const actual = sut.getResults(filePath) expect(actual).toHaveLength(2) - expect(actual.every(r => r.status === TestReconciliationState.Unknown)).toEqual(true) - expect(actual.every(r => r.shortMessage && r.shortMessage.includes('duplicate test names'))).toEqual(true) + expect(actual.every((r) => r.status === TestReconciliationState.Unknown)).toEqual(true) + expect(actual.every((r) => r.shortMessage && r.shortMessage.includes('duplicate test names'))).toEqual(true) }) }) it('should only mark error line number if it is within the right itBlock', () => { @@ -257,10 +257,12 @@ describe('TestResultProvider', () => { const actual = sut.getResults(filePath) expect(actual).toHaveLength(2) - expect(actual.some(a => a.name === testBlock.name && a.status === TestReconciliationState.Unknown)).toEqual(true) + expect(actual.some((a) => a.name === testBlock.name && a.status === TestReconciliationState.Unknown)).toEqual( + true + ) expect( actual.some( - a => + (a) => a.name === testBlock2.name && a.status === TestReconciliationState.KnownSuccess && a.lineNumberOfError === testBlock2.end.line - 1 @@ -304,12 +306,12 @@ describe('TestResultProvider', () => { const actual = sut.getResults(filePath) expect(actual).toHaveLength(2) - expect(actual.some(a => a.status === TestReconciliationState.Unknown && a.name === testBlock.name)).toEqual( - true - ) - expect(actual.some(a => a.status === TestReconciliationState.KnownFail && a.name === testBlock2.name)).toEqual( + expect(actual.some((a) => a.status === TestReconciliationState.Unknown && a.name === testBlock.name)).toEqual( true ) + expect( + actual.some((a) => a.status === TestReconciliationState.KnownFail && a.name === testBlock2.name) + ).toEqual(true) }) it(`find test by assertion location`, () => { const sut = new TestResultProvider() @@ -325,11 +327,11 @@ describe('TestResultProvider', () => { const actual = sut.getResults(filePath) expect(actual).toHaveLength(2) - expect(actual.some(a => a.status === TestReconciliationState.Unknown && a.name === testBlock.name)).toEqual( + expect(actual.some((a) => a.status === TestReconciliationState.Unknown && a.name === testBlock.name)).toEqual( true ) expect( - actual.some(a => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock2.name) + actual.some((a) => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock2.name) ).toEqual(true) }) it(`find test by partial name match`, () => { @@ -348,11 +350,11 @@ describe('TestResultProvider', () => { ]) const actual = sut.getResults(filePath) expect(actual).toHaveLength(2) - expect(actual.some(a => a.status === TestReconciliationState.Unknown && a.name === testBlock.name)).toEqual( + expect(actual.some((a) => a.status === TestReconciliationState.Unknown && a.name === testBlock.name)).toEqual( true ) expect( - actual.some(a => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock2.name) + actual.some((a) => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock2.name) ).toEqual(true) }) it(`multiple template literals`, () => { @@ -373,11 +375,11 @@ describe('TestResultProvider', () => { const actual = sut.getResults(filePath) expect(actual).toHaveLength(2) expect( - actual.some(a => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock3.name) + actual.some((a) => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock3.name) + ).toEqual(true) + expect( + actual.some((a) => a.status === TestReconciliationState.KnownFail && a.name === testBlock2.name) ).toEqual(true) - expect(actual.some(a => a.status === TestReconciliationState.KnownFail && a.name === testBlock2.name)).toEqual( - true - ) }) describe('when match failed', () => { diff --git a/tests/TestResults/index.test.ts b/tests/TestResults/index.test.ts index e83b08c32..4efc83e15 100644 --- a/tests/TestResults/index.test.ts +++ b/tests/TestResults/index.test.ts @@ -1,9 +1,9 @@ -jest.disableAutomock() - -describe('TestResults', () => { - describe('module.exports', () => { - it('should match the snapshot', () => { - expect(require('../../src/TestResults')).toMatchSnapshot() - }) - }) -}) +jest.disableAutomock() + +describe('TestResults', () => { + describe('module.exports', () => { + it('should match the snapshot', () => { + expect(require('../../src/TestResults')).toMatchSnapshot() + }) + }) +}) diff --git a/tests/diagnostics.test.ts b/tests/diagnostics.test.ts index c7ea469ee..5d161e9e4 100644 --- a/tests/diagnostics.test.ts +++ b/tests/diagnostics.test.ts @@ -134,8 +134,8 @@ describe('test diagnostics', () => { const failedTestSuiteCount = allTests.reduce((sum, t) => sum + (t.status === 'KnownFail' ? 1 : 0), 0) const notFailedTestSuiteCount = allTests.reduce((sum, t) => sum + (t.status !== 'KnownFail' ? 1 : 0), 0) const failedAssertionCount = allTests - .filter(t => t.status === 'KnownFail') - .map(f => f.assertions.filter(a => (a.status = 'KnownFail'))) + .filter((t) => t.status === 'KnownFail') + .map((f) => f.assertions.filter((a) => (a.status = 'KnownFail'))) .reduce((sum, assertions) => sum + assertions.length, 0) const failedTestWithoutAssertionCount = allTests.reduce( @@ -172,7 +172,7 @@ describe('test diagnostics', () => { validateRange(rCall, 0, 0) assertion++ } else { - f.assertions.forEach(a => { + f.assertions.forEach((a) => { const rCall = rangeCalls[assertion] const dCall = diagCalls[assertion] @@ -189,7 +189,7 @@ describe('test diagnostics', () => { it('knows how many failed suite from diagnostics', () => { const mockDiagnostics = new MockDiagnosticCollection() const invokeCount = 7 - mockDiagnostics.forEach.mockImplementation(f => { + mockDiagnostics.forEach.mockImplementation((f) => { for (let i = 0; i < invokeCount; i++) { f({}) } @@ -204,7 +204,7 @@ describe('test diagnostics', () => { const invalidLine = [0, -1, undefined, null, NaN] console.warn = jest.fn() - invalidLine.forEach(line => { + invalidLine.forEach((line) => { jest.clearAllMocks() assertion.line = line diff --git a/tests/extension.test.ts b/tests/extension.test.ts index 1a8bd026b..4eab0e502 100644 --- a/tests/extension.test.ts +++ b/tests/extension.test.ts @@ -116,7 +116,7 @@ describe('Extension', () => { it('to start extension', () => { activate(context) - const callArg = context.subscriptions.push.mock.calls[0].find(args => { + const callArg = context.subscriptions.push.mock.calls[0].find((args) => { return args[0] === `${extensionName}.start` }) @@ -127,7 +127,7 @@ describe('Extension', () => { it('to stop extension', () => { activate(context) - const callArg = context.subscriptions.push.mock.calls[0].find(args => { + const callArg = context.subscriptions.push.mock.calls[0].find((args) => { return args[0] === `${extensionName}.stop` }) @@ -138,7 +138,7 @@ describe('Extension', () => { it('to restart extension', () => { activate(context) - const callArg = context.subscriptions.push.mock.calls[0].find(args => { + const callArg = context.subscriptions.push.mock.calls[0].find((args) => { return args[0] === `${extensionName}.restart` }) @@ -149,7 +149,7 @@ describe('Extension', () => { it('to toggle the coverage overlay visibility', () => { activate(context) - const callArg = context.subscriptions.push.mock.calls[0].find(args => { + const callArg = context.subscriptions.push.mock.calls[0].find((args) => { return args[0] === `${extensionName}.coverage.toggle` }) @@ -160,7 +160,7 @@ describe('Extension', () => { it('to run specific test', () => { activate(context) - const callArg = context.subscriptions.push.mock.calls[0].find(args => { + const callArg = context.subscriptions.push.mock.calls[0].find((args) => { return args[0] === `${extensionName}.run-test` }) @@ -177,8 +177,8 @@ describe('Extension', () => { activate(context) expect(register).toHaveBeenCalledTimes(2) - const registeredAsNode = register.mock.calls.some(parameters => parameters[0] === 'node') - const registeredAsJestTest = register.mock.calls.some(parameters => parameters[0] === 'vscode-jest-tests') + const registeredAsNode = register.mock.calls.some((parameters) => parameters[0] === 'node') + const registeredAsJestTest = register.mock.calls.some((parameters) => parameters[0] === 'vscode-jest-tests') expect(registeredAsNode && registeredAsJestTest).toBeTruthy() }) }) diff --git a/tests/extensionManager.test.ts b/tests/extensionManager.test.ts index eff10602b..4e1f4c51b 100644 --- a/tests/extensionManager.test.ts +++ b/tests/extensionManager.test.ts @@ -17,7 +17,7 @@ import { TestState } from '../src/DebugCodeLens' import { readFileSync } from 'fs' import { IPluginWindowSettings } from '../src/Settings' -vscode.workspace.getConfiguration = jest.fn().mockImplementation(section => { +vscode.workspace.getConfiguration = jest.fn().mockImplementation((section) => { const data = readFileSync('./package.json') const config = JSON.parse(data.toString()).contributes.configuration.properties @@ -29,7 +29,7 @@ vscode.workspace.getConfiguration = jest.fn().mockImplementation(section => { } return { - get: jest.fn().mockImplementation(key => defaults[`${section}.${key}`]), + get: jest.fn().mockImplementation((key) => defaults[`${section}.${key}`]), } }) @@ -45,7 +45,9 @@ describe('InstancesManager', () => { extensionManager = new ExtensionManager({} as any) registerSpy.mockClear() unregisterSpy.mockClear() - ;(vscode.workspace as any).workspaceFolders = [{ uri: { fsPath: 'workspaceFolder1' }, name: 'workspaceFolder1' }] as any + ;(vscode.workspace as any).workspaceFolders = [ + { uri: { fsPath: 'workspaceFolder1' }, name: 'workspaceFolder1' }, + ] as any ;(jestInstance.deactivate as any).mockReset() ;(vscode.window.showWorkspaceFolderPick as any).mockReset() ;(vscode.commands.registerCommand as any).mockReset() @@ -174,7 +176,9 @@ describe('InstancesManager', () => { describe('get()', () => { afterEach(() => { - (vscode.workspace as any).workspaceFolders = [{ uri: { fsPath: 'workspaceFolder1' }, name: 'workspaceFolder1' }] as any + ;(vscode.workspace as any).workspaceFolders = [ + { uri: { fsPath: 'workspaceFolder1' }, name: 'workspaceFolder1' }, + ] as any }) it('should return extension at once if there is only one workspace folder', async () => { @@ -191,7 +195,7 @@ describe('InstancesManager', () => { }) it('should return undefined if no workspace selected', async () => { - (vscode.workspace as any).workspaceFolders = [{ name: 'workspaceFolder1' }, { name: 'workspaceFolder2' }] as any + ;(vscode.workspace as any).workspaceFolders = [{ name: 'workspaceFolder1' }, { name: 'workspaceFolder2' }] as any ;(vscode.window.showWorkspaceFolderPick as any).mockReturnValue(undefined) expect(await extensionManager.get()).toBeUndefined() }) @@ -212,7 +216,7 @@ describe('InstancesManager', () => { it('should register command and preserve context', async () => { const command = 'command' const thisArg = {} - const callback = jest.fn(function() { + const callback = jest.fn(function () { expect(this).toBe(thisArg) }) ;(vscode.commands.registerCommand as any).mockImplementation((_command, _callback) => { diff --git a/tests/helpers.test.ts b/tests/helpers.test.ts index 866bbd581..ca88fadc2 100644 --- a/tests/helpers.test.ts +++ b/tests/helpers.test.ts @@ -98,14 +98,14 @@ describe('ModuleHelpers', () => { const expected = 'node_modules/.bin/jest.TEST' mockJoin.mockImplementation(require.requireActual('path').posix.join) - mockNormalize.mockImplementation(arg => arg) - mockExistsSync.mockImplementation(path => path === expected) + mockNormalize.mockImplementation((arg) => arg) + mockExistsSync.mockImplementation((path) => path === expected) expect(pathToJest(defaultSettings)).toBe(`"${expected}"`) }) it('default jestToPath path can preserve special characters', () => { mockJoin.mockImplementation(require.requireActual('path').posix.join) - mockNormalize.mockImplementation(arg => arg) + mockNormalize.mockImplementation((arg) => arg) const testPaths = [ '/root/my dir/space', @@ -114,10 +114,10 @@ describe('ModuleHelpers', () => { '/root/外國人/unicode', '/root/\\space/double-escape', ] - testPaths.forEach(p => { + testPaths.forEach((p) => { const settings = { ...defaultSettings, rootPath: p } const expected = `${p}/node_modules/.bin/jest.TEST` - mockExistsSync.mockImplementation(path => path === expected) + mockExistsSync.mockImplementation((path) => path === expected) expect(pathToJest(settings)).toBe(`"${expected}"`) }) }) @@ -125,8 +125,8 @@ describe('ModuleHelpers', () => { const expected = '"jest.TEST"' mockJoin.mockImplementation(require.requireActual('path').posix.join) - mockNormalize.mockImplementation(arg => arg) - mockExistsSync.mockImplementation(_ => false) + mockNormalize.mockImplementation((arg) => arg) + mockExistsSync.mockImplementation((_) => false) expect(pathToJest(defaultSettings)).toBe(expected) }) From c332cc7eb9f354da50a316721fecd33495250426 Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Mon, 11 May 2020 10:26:35 -0400 Subject: [PATCH 02/11] adding .gitattribute to spcify eol for all platform --- src/.gitattributes | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 src/.gitattributes diff --git a/src/.gitattributes b/src/.gitattributes new file mode 100644 index 000000000..4cb9857bf --- /dev/null +++ b/src/.gitattributes @@ -0,0 +1,2 @@ +# all tex files will be checked out with LF as eol +* text=auto eol=lf \ No newline at end of file From afdd76671c998e7fdfd5400c8e3aa1a6b3503dce Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Mon, 11 May 2020 10:48:34 -0400 Subject: [PATCH 03/11] move .gitattributes to the right location --- src/.gitattributes => .gitattributes | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/.gitattributes => .gitattributes (100%) diff --git a/src/.gitattributes b/.gitattributes similarity index 100% rename from src/.gitattributes rename to .gitattributes From 7ae6d80f284d850744ee026c6abeec59575ea654 Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Mon, 11 May 2020 11:57:38 -0400 Subject: [PATCH 04/11] Update .travis.yml --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 96a58904b..00cce454b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,10 @@ stages: env: - YARN_GPG=no +## disable eol conversion upon clone +git: + autocrlf: input + jobs: - stage: "verify" name: "verify" From 552510a6acfe933f02c019dee312e27da0d38799 Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Mon, 11 May 2020 16:06:06 -0400 Subject: [PATCH 05/11] fix prettier on windows --- .gitattributes | 4 ++-- package.json | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitattributes b/.gitattributes index 4cb9857bf..1be2807aa 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ -# all tex files will be checked out with LF as eol -* text=auto eol=lf \ No newline at end of file +# all text files will be checked in with LF as eol +* text=auto \ No newline at end of file diff --git a/package.json b/package.json index ca8db183d..aa86b9c2b 100644 --- a/package.json +++ b/package.json @@ -277,12 +277,12 @@ "vscode:prepublish": "yarn clean-out && yarn compile", "compile": "webpack --mode production", "watch": "webpack --mode development --watch --info-verbosity verbose", - "lint": "tslint -c tslint.json 'src/**/*.ts' 'tests/**/*.ts'", + "lint": "tslint -c tslint.json \"src/**/*.ts\" \"tests/**/*.ts\"", "test": "jest", "watch-test": "yarn test -- --watch", "prettier": "prettier", - "prettier-project-check": "yarn prettier --check '?(__mocks__|src|tests)/**/*.ts' '*.json'", - "prettier-project-write": "yarn prettier --write '?(__mocks__|src|tests)/**/*.ts' '*.json'" + "prettier-project-check": "yarn prettier --check \"?(__mocks__|src|tests)/**/*.ts\" \"json\"", + "prettier-project-write": "yarn prettier --write \"?(__mocks__|src|tests)/**/*.ts\" \"json\"" }, "dependencies": { "istanbul-lib-coverage": "^1.1.1", From d36a3e2599ef1795bf997d85a9511ceaf8272bd7 Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Mon, 11 May 2020 16:26:10 -0400 Subject: [PATCH 06/11] fix typo --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index aa86b9c2b..f7da7c921 100644 --- a/package.json +++ b/package.json @@ -281,8 +281,8 @@ "test": "jest", "watch-test": "yarn test -- --watch", "prettier": "prettier", - "prettier-project-check": "yarn prettier --check \"?(__mocks__|src|tests)/**/*.ts\" \"json\"", - "prettier-project-write": "yarn prettier --write \"?(__mocks__|src|tests)/**/*.ts\" \"json\"" + "prettier-project-check": "yarn prettier --check \"?(__mocks__|src|tests)/**/*.ts\" \"*.json\"", + "prettier-project-write": "yarn prettier --write \"?(__mocks__|src|tests)/**/*.ts\" \"*.json\"" }, "dependencies": { "istanbul-lib-coverage": "^1.1.1", From 7e8e3a6b3408d2970e36ac60afbc42ad3100efe7 Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Sat, 16 May 2020 15:54:07 -0400 Subject: [PATCH 07/11] eslint migration --- .eslintrc.js | 21 + .vscode/settings.json | 12 +- .vscodeignore | 6 +- CHANGELOG.md | 2 +- package.json | 13 +- prettier.config.js | 18 +- src/Coverage/Formatters/DefaultFormatter.ts | 26 +- .../Formatters/GutterFormatter/index.ts | 6 +- src/Coverage/Formatters/helpers.ts | 8 +- src/DebugConfigurationProvider.ts | 4 +- src/JestExt.ts | 10 +- .../JestProcessManager.ts | 4 +- src/Settings/index.ts | 4 +- .../SnapshotCodeLensProvider.ts | 24 +- src/StatusBar.ts | 3 +- src/TestResults/TestResult.ts | 112 +-- src/diagnostics.ts | 12 +- src/extensionManager.ts | 64 +- src/helpers.ts | 7 +- src/messaging.ts | 39 +- tests/.eslintrc.js | 8 + .../Coverage/CoverageCodeLensProvider.test.ts | 2 +- tests/Coverage/CoverageMapProvider.test.ts | 1 + .../Formatters/GutterFormatter.test.ts | 4 +- tests/Coverage/index.test.ts | 3 +- .../DebugCodeLensProvider.test.ts | 4 +- tests/JestExt.test.ts | 10 +- .../JestProcessManagement/JestProcess.test.ts | 9 +- tests/StatusBar.test.ts | 1 + tests/TestResults/TestResult.test.ts | 39 +- tests/TestResults/index.test.ts | 3 +- tests/decorations.test.ts | 22 +- tests/extensionManager.test.ts | 8 +- tests/helpers.test.ts | 2 +- tests/messaging.test.ts | 4 +- tslint.json | 22 - yarn.lock | 715 +++++++++++++++--- 37 files changed, 871 insertions(+), 381 deletions(-) create mode 100644 .eslintrc.js create mode 100644 tests/.eslintrc.js delete mode 100644 tslint.json diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 000000000..387dce9d2 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,21 @@ +module.exports = { + root: true, + env: { + "es6": true, + "node": true, + "jest/globals": true, + }, + parser: '@typescript-eslint/parser', + plugins: [ + '@typescript-eslint', + 'jest', + ], + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:jest/recommended', + 'prettier', + 'prettier/@typescript-eslint', + ], +}; \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 82127e99a..cf069b1b4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,16 +4,14 @@ "node_modules/jest-editor-support/src": false, "coverage/": true }, - "tslint.exclude": "./integrations/*", "search.exclude": { "out": true, "node_modules/jest-editor-support/src": false }, "typescript.tsdk": "./node_modules/typescript/lib", - "eslint.enable": false, - "tslint.autoFixOnSave": true, - "prettier.semi": false, - "prettier.printWidth": 120, - "prettier.trailingComma": "es5", - "prettier.singleQuote": true + // eslint + "eslint.enable": true, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + } } diff --git a/.vscodeignore b/.vscodeignore index 2866ca4d2..697a744c3 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -10,7 +10,9 @@ tsconfig.json **/tsconfig.json jsconfig.json jest.config.js -tslint.json +.eslintrc.js +**/.eslintrc.js +prettier.config.js .travis.yml yarn.lock yarn-error.log @@ -20,4 +22,4 @@ coverage images/** !images/vscode-jest.png node_modules -webpack.config.js \ No newline at end of file +webpack.config.js diff --git a/CHANGELOG.md b/CHANGELOG.md index c48cb2477..2d3f3e5f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ Bug-fixes within the same version aren't needed * improve create-react-app detection logic - stephtr * improve the detection of cases in which Jest needs to be restarted with `--watchAll` - [@lordofthelake](https://github.com/lordofthelake) * upgrade all dependencies to the latest, except istanbul-lib-xxx, which requires more code change and will be handled in a separate coverage PR. - @connectdotz -* run prettier for the whole project and add prettier check in ci script - @connectdotz +* code base clean up: migrate from tslint to eslint and adopted the latest recommended coding style, adopt semi-colon, added more ci check... - @connectdotz --> ### 3.2.0 diff --git a/package.json b/package.json index f7da7c921..47548c0b3 100644 --- a/package.json +++ b/package.json @@ -277,7 +277,7 @@ "vscode:prepublish": "yarn clean-out && yarn compile", "compile": "webpack --mode production", "watch": "webpack --mode development --watch --info-verbosity verbose", - "lint": "tslint -c tslint.json \"src/**/*.ts\" \"tests/**/*.ts\"", + "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"", "test": "jest", "watch-test": "yarn test -- --watch", "prettier": "prettier", @@ -294,18 +294,23 @@ "@types/jest": "^25.2.1", "@types/node": "^8.0.31", "@types/vscode": "^1.23.0", + "@typescript-eslint/eslint-plugin": "^2.32.0", + "@typescript-eslint/parser": "^2.32.0", "coveralls": "^3.1.0", "danger": "^10.1.1", + "eslint": "^7.0.0", + "eslint-config-prettier": "^6.11.0", + "eslint-plugin-import": "^2.20.2", + "eslint-plugin-jest": "^23.10.0", + "eslint-plugin-jsdoc": "^25.2.0", + "eslint-plugin-prefer-arrow": "^1.2.1", "jest": "^25.5.0", "lint-staged": "^10.2.0", "prettier": "^2.0.5", "rimraf": "^3.0.2", "ts-jest": "^25.4.0", "ts-loader": "^7.0.1", - "tslint": "^6.1.2", - "tslint-config-prettier": "^1.18.0", "typescript": "^3.8.3", - "typescript-tslint-plugin": "^0.5.5", "vscode-test": "^1.3.0", "webpack": "^4.43.0", "webpack-cli": "^3.3.11" diff --git a/prettier.config.js b/prettier.config.js index 193379769..ba0e3dee8 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1,14 +1,14 @@ module.exports = { - trailingComma: "es5", + trailingComma: 'es5', printWidth: 120, semi: false, singleQuote: true, - "overrides": [ + overrides: [ { - "files": "*.ts", - "options": { - "parser": "typescript" - } - } - ] -}; \ No newline at end of file + files: '*.ts', + options: { + parser: 'typescript', + }, + }, + ], +}; diff --git a/src/Coverage/Formatters/DefaultFormatter.ts b/src/Coverage/Formatters/DefaultFormatter.ts index 57f335596..e1d62bcd9 100644 --- a/src/Coverage/Formatters/DefaultFormatter.ts +++ b/src/Coverage/Formatters/DefaultFormatter.ts @@ -3,6 +3,19 @@ import * as vscode from 'vscode' import { FileCoverage } from 'istanbul-lib-coverage' import { isValidLocation } from './helpers' +const uncoveredBranch = vscode.window.createTextEditorDecorationType({ + backgroundColor: 'rgba(216,134,123,0.4)', + overviewRulerColor: 'rgba(216,134,123,0.8)', + overviewRulerLane: vscode.OverviewRulerLane.Left, +}) + +const uncoveredLine = vscode.window.createTextEditorDecorationType({ + isWholeLine: true, + backgroundColor: 'rgba(216,134,123,0.4)', + overviewRulerColor: 'rgba(216,134,123,0.8)', + overviewRulerLane: vscode.OverviewRulerLane.Left, +}) + export class DefaultFormatter extends AbstractFormatter { format(editor: vscode.TextEditor) { const fileCoverage = this.coverageMapProvider.getFileCoverage(editor.document.fileName) @@ -56,16 +69,3 @@ export class DefaultFormatter extends AbstractFormatter { editor.setDecorations(uncoveredBranch, []) } } - -const uncoveredBranch = vscode.window.createTextEditorDecorationType({ - backgroundColor: 'rgba(216,134,123,0.4)', - overviewRulerColor: 'rgba(216,134,123,0.8)', - overviewRulerLane: vscode.OverviewRulerLane.Left, -}) - -const uncoveredLine = vscode.window.createTextEditorDecorationType({ - isWholeLine: true, - backgroundColor: 'rgba(216,134,123,0.4)', - overviewRulerColor: 'rgba(216,134,123,0.8)', - overviewRulerLane: vscode.OverviewRulerLane.Left, -}) diff --git a/src/Coverage/Formatters/GutterFormatter/index.ts b/src/Coverage/Formatters/GutterFormatter/index.ts index b34d45a82..0a9a3ca85 100644 --- a/src/Coverage/Formatters/GutterFormatter/index.ts +++ b/src/Coverage/Formatters/GutterFormatter/index.ts @@ -4,7 +4,7 @@ import * as vscode from 'vscode' import { FileCoverage } from 'istanbul-lib-coverage' import { isValidLocation } from '../helpers' -export interface ICoverageLines { +export interface CoverageLines { covered: vscode.Range[] partiallyCovered: vscode.Range[] uncovered: vscode.Range[] @@ -57,8 +57,8 @@ export class GutterFormatter extends AbstractFormatter { editor.setDecorations(this.partiallyCoveredLine, coverageFormatting.partiallyCovered) } - computeFormatting(editor: vscode.TextEditor, fileCoverage: FileCoverage): ICoverageLines { - const coverageFormatting: ICoverageLines = { + computeFormatting(editor: vscode.TextEditor, fileCoverage: FileCoverage): CoverageLines { + const coverageFormatting: CoverageLines = { covered: [], partiallyCovered: [], uncovered: [], diff --git a/src/Coverage/Formatters/helpers.ts b/src/Coverage/Formatters/helpers.ts index b9623b3cf..55ae0d79b 100644 --- a/src/Coverage/Formatters/helpers.ts +++ b/src/Coverage/Formatters/helpers.ts @@ -1,9 +1,9 @@ import { Location, Position } from 'istanbul-lib-coverage' -export function isValidLocation(l: Location) { - return isValidPosition(l.start) && isValidPosition(l.end) -} - export function isValidPosition(p: Position) { return (p || false) && p.line !== null && p.line >= 0 } + +export function isValidLocation(l: Location) { + return isValidPosition(l.start) && isValidPosition(l.end) +} diff --git a/src/DebugConfigurationProvider.ts b/src/DebugConfigurationProvider.ts index 531a514a7..a9ba6c9e4 100644 --- a/src/DebugConfigurationProvider.ts +++ b/src/DebugConfigurationProvider.ts @@ -2,8 +2,8 @@ import * as vscode from 'vscode' import { getTestCommand, isCreateReactAppTestCommand } from './helpers' export class DebugConfigurationProvider implements vscode.DebugConfigurationProvider { - private fileNameToRun: string = '' - private testToRun: string = '' + private fileNameToRun = '' + private testToRun = '' /** * Prepares injecting the name of the test, which has to be debugged, into the `DebugConfiguration`, diff --git a/src/JestExt.ts b/src/JestExt.ts index b27762705..278a4b4c8 100644 --- a/src/JestExt.ts +++ b/src/JestExt.ts @@ -2,7 +2,7 @@ import * as vscode from 'vscode' import { ProjectWorkspace, JestTotalResults } from 'jest-editor-support' import * as decorations from './decorations' -import { IPluginResourceSettings } from './Settings' +import { PluginResourceSettings } from './Settings' import { statusBar, Status, StatusBar, Mode } from './StatusBar' import { TestReconciliationState, @@ -42,7 +42,7 @@ export class JestExt { failingAssertionDecorators: { [fileName: string]: vscode.TextEditorDecorationType[] } private jestWorkspace: ProjectWorkspace - private pluginSettings: IPluginResourceSettings + private pluginSettings: PluginResourceSettings private workspaceFolder: vscode.WorkspaceFolder private instanceSettings: InstanceSettings @@ -68,7 +68,7 @@ export class JestExt { workspaceFolder: vscode.WorkspaceFolder, jestWorkspace: ProjectWorkspace, outputChannel: vscode.OutputChannel, - pluginSettings: IPluginResourceSettings, + pluginSettings: PluginResourceSettings, debugCodeLensProvider: DebugCodeLensProvider, debugConfigurationProvider: DebugConfigurationProvider, failDiagnostics: vscode.DiagnosticCollection, @@ -189,7 +189,7 @@ export class JestExt { this.parsingTestFile = false } - public triggerUpdateSettings(updatedSettings: IPluginResourceSettings) { + public triggerUpdateSettings(updatedSettings: PluginResourceSettings) { this.pluginSettings = updatedSettings this.jestWorkspace.rootPath = updatedSettings.rootPath @@ -451,7 +451,7 @@ export class JestExt { this.updateStatusBar('initial', undefined, false) } - private updateStatusBar(status: Status, details?: string, watchMode: boolean = true) { + private updateStatusBar(status: Status, details?: string, watchMode = true) { const modes: Mode[] = [] if (this.coverageOverlay.enabled) { modes.push('coverage') diff --git a/src/JestProcessManagement/JestProcessManager.ts b/src/JestProcessManagement/JestProcessManager.ts index 2844e3419..189fdae1b 100644 --- a/src/JestProcessManagement/JestProcessManager.ts +++ b/src/JestProcessManagement/JestProcessManager.ts @@ -21,7 +21,9 @@ export class JestProcessManager { } public startJestProcess({ - exitCallback = () => {}, + exitCallback = () => { + /* do nothing */ + }, watchMode = WatchMode.None, keepAlive = false, }: { diff --git a/src/Settings/index.ts b/src/Settings/index.ts index 07a63a41b..4e3895b2c 100644 --- a/src/Settings/index.ts +++ b/src/Settings/index.ts @@ -1,6 +1,6 @@ import { TestState } from '../DebugCodeLens' -export interface IPluginResourceSettings { +export interface PluginResourceSettings { autoEnable?: boolean enableInlineErrorMessages?: boolean enableSnapshotUpdateMessages?: boolean @@ -14,7 +14,7 @@ export interface IPluginResourceSettings { debugMode?: boolean } -export interface IPluginWindowSettings { +export interface PluginWindowSettings { debugCodeLens: { enabled: boolean showWhenTestStateIn: TestState[] diff --git a/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts b/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts index 0b6887bb1..fead796a3 100644 --- a/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts +++ b/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts @@ -6,18 +6,6 @@ import { previewCommand } from './SnapshotPreviewProvider' const missingSnapshotCommand = `${extensionName}.snapshot.missing` -export function registerSnapshotCodeLens(enableSnapshotPreviews: boolean) { - if (!enableSnapshotPreviews) { - return [] - } - return [ - vscode.languages.registerCodeLensProvider({ pattern: '**/*.{ts,tsx,js,jsx}' }, new SnapshotCodeLensProvider()), - vscode.commands.registerCommand(missingSnapshotCommand, () => { - vscode.window.showInformationMessage('Run test to generate snapshot.') - }), - ] -} - class SnapshotCodeLensProvider implements vscode.CodeLensProvider { public provideCodeLenses(document: vscode.TextDocument, _token: vscode.CancellationToken) { const snapshots = new Snapshot() @@ -42,3 +30,15 @@ class SnapshotCodeLensProvider implements vscode.CodeLensProvider { }) } } + +export function registerSnapshotCodeLens(enableSnapshotPreviews: boolean) { + if (!enableSnapshotPreviews) { + return [] + } + return [ + vscode.languages.registerCodeLensProvider({ pattern: '**/*.{ts,tsx,js,jsx}' }, new SnapshotCodeLensProvider()), + vscode.commands.registerCommand(missingSnapshotCommand, () => { + vscode.window.showInformationMessage('Run test to generate snapshot.') + }), + ] +} diff --git a/src/StatusBar.ts b/src/StatusBar.ts index 32fd02cf1..e95ef00ed 100644 --- a/src/StatusBar.ts +++ b/src/StatusBar.ts @@ -188,13 +188,14 @@ export class StatusBar { const message = this.getMessageByStatus(request.status) switch (statusBarItem.type) { - case StatusType.active: + case StatusType.active: { const modes = this.getModes(request.modes) const details = !this.needsSummaryStatus() && request.details ? request.details : '' const displayString = [message, details, modes].filter((s) => s && s.length > 0).join(' ') statusBarItem.text = `Jest: ${displayString}` statusBarItem.tooltip = `Jest status of '${this.activeFolder}'` break + } case StatusType.summary: statusBarItem.text = `Jest-WS: ${message}` break diff --git a/src/TestResults/TestResult.ts b/src/TestResults/TestResult.ts index 4522dcd6b..175aa3aa0 100644 --- a/src/TestResults/TestResult.ts +++ b/src/TestResults/TestResult.ts @@ -25,6 +25,60 @@ export interface TestResult { lineNumberOfError?: number } +export const withLowerCaseWindowsDriveLetter = (filePath: string): string | undefined => { + const match = filePath.match(/^([A-Z]:\\)(.*)$/) + if (match) { + return `${match[1].toLowerCase()}${match[2]}` + } +} + +function testResultWithLowerCaseWindowsDriveLetter(testResult: JestFileResults): JestFileResults { + const newFilePath = withLowerCaseWindowsDriveLetter(testResult.name) + if (newFilePath) { + return { + ...testResult, + name: newFilePath, + } + } + + return testResult +} + +export const testResultsWithLowerCaseWindowsDriveLetters = (testResults: JestFileResults[]): JestFileResults[] => { + if (!testResults) { + return testResults + } + + return testResults.map(testResultWithLowerCaseWindowsDriveLetter) +} + +function fileCoverageWithLowerCaseWindowsDriveLetter(fileCoverage: FileCoverage) { + const newFilePath = withLowerCaseWindowsDriveLetter(fileCoverage.path) + if (newFilePath) { + return { + ...fileCoverage, + path: newFilePath, + } + } + + return fileCoverage +} + +export const coverageMapWithLowerCaseWindowsDriveLetters = (data: JestTotalResults) => { + if (!data.coverageMap) { + return + } + + const result = {} + const filePaths = Object.keys(data.coverageMap) + for (const filePath of filePaths) { + const newFileCoverage = fileCoverageWithLowerCaseWindowsDriveLetter(data.coverageMap[filePath]) + result[newFileCoverage.path] = newFileCoverage + } + + return result +} + /** * Normalize file paths on Windows systems to use lowercase drive letters. * This follows the standard used by Visual Studio Code for URIs which includes @@ -32,7 +86,7 @@ export interface TestResult { * * @param data Parsed JSON results */ -export function resultsWithLowerCaseWindowsDriveLetters(data: JestTotalResults) { +export const resultsWithLowerCaseWindowsDriveLetters = (data: JestTotalResults) => { if (path.sep === '\\') { return { ...data, @@ -47,7 +101,7 @@ export function resultsWithLowerCaseWindowsDriveLetters(data: JestTotalResults) /** * Removes ANSI escape sequence characters from test results in order to get clean messages */ -export function resultsWithoutAnsiEscapeSequence(data: JestTotalResults) { +export const resultsWithoutAnsiEscapeSequence = (data: JestTotalResults) => { if (!data || !data.testResults) { return data } @@ -64,57 +118,3 @@ export function resultsWithoutAnsiEscapeSequence(data: JestTotalResults) { })), } } - -export function coverageMapWithLowerCaseWindowsDriveLetters(data: JestTotalResults) { - if (!data.coverageMap) { - return - } - - const result = {} - const filePaths = Object.keys(data.coverageMap) - for (const filePath of filePaths) { - const newFileCoverage = fileCoverageWithLowerCaseWindowsDriveLetter(data.coverageMap[filePath]) - result[newFileCoverage.path] = newFileCoverage - } - - return result -} - -function fileCoverageWithLowerCaseWindowsDriveLetter(fileCoverage: FileCoverage) { - const newFilePath = withLowerCaseWindowsDriveLetter(fileCoverage.path) - if (newFilePath) { - return { - ...fileCoverage, - path: newFilePath, - } - } - - return fileCoverage -} - -export function testResultsWithLowerCaseWindowsDriveLetters(testResults: JestFileResults[]): JestFileResults[] { - if (!testResults) { - return testResults - } - - return testResults.map(testResultWithLowerCaseWindowsDriveLetter) -} - -function testResultWithLowerCaseWindowsDriveLetter(testResult: JestFileResults): JestFileResults { - const newFilePath = withLowerCaseWindowsDriveLetter(testResult.name) - if (newFilePath) { - return { - ...testResult, - name: newFilePath, - } - } - - return testResult -} - -export function withLowerCaseWindowsDriveLetter(filePath: string): string | undefined { - const match = filePath.match(/^([A-Z]:\\)(.*)$/) - if (match) { - return `${match[1].toLowerCase()}${match[2]}` - } -} diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 96441063d..9f744b87a 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -8,6 +8,12 @@ import { existsSync } from 'fs' import { TestFileAssertionStatus } from 'jest-editor-support' import { TestReconciliationState, TestResult } from './TestResults' +function createDiagnosticWithRange(message: string, range: vscode.Range): vscode.Diagnostic { + const diag = new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Error) + diag.source = 'Jest' + return diag +} + function createDiagnostic( message: string, lineNumber: number, @@ -18,12 +24,6 @@ function createDiagnostic( return createDiagnosticWithRange(message, new vscode.Range(line, startCol, line, endCol)) } -function createDiagnosticWithRange(message: string, range: vscode.Range): vscode.Diagnostic { - const diag = new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Error) - diag.source = 'Jest' - return diag -} - // update diagnostics for the active editor // it will utilize the parsed test result to mark actual text position. export function updateCurrentDiagnostics( diff --git a/src/extensionManager.ts b/src/extensionManager.ts index 076c4a12e..0590fd5bd 100644 --- a/src/extensionManager.ts +++ b/src/extensionManager.ts @@ -5,18 +5,47 @@ import { pathToJest, pathToConfig } from './helpers' import { JestExt } from './JestExt' import { DebugCodeLensProvider, TestState } from './DebugCodeLens' import { DebugConfigurationProvider } from './DebugConfigurationProvider' -import { IPluginResourceSettings, IPluginWindowSettings } from './Settings' +import { PluginResourceSettings, PluginWindowSettings } from './Settings' import { statusBar } from './StatusBar' export type GetJestExtByURI = (uri: vscode.Uri) => JestExt | undefined +export function getExtensionWindowSettings(): PluginWindowSettings { + const config = vscode.workspace.getConfiguration('jest') + return { + debugCodeLens: { + enabled: config.get<boolean>('enableCodeLens'), + showWhenTestStateIn: config.get<TestState[]>('debugCodeLens.showWhenTestStateIn'), + }, + enableSnapshotPreviews: config.get<boolean>('enableSnapshotPreviews'), + disabledWorkspaceFolders: config.get<string[]>('disabledWorkspaceFolders'), + } +} + +export function getExtensionResourceSettings(uri: vscode.Uri): PluginResourceSettings { + const config = vscode.workspace.getConfiguration('jest', uri) + return { + autoEnable: config.get<boolean>('autoEnable'), + enableInlineErrorMessages: config.get<boolean>('enableInlineErrorMessages'), + enableSnapshotUpdateMessages: config.get<boolean>('enableSnapshotUpdateMessages'), + pathToConfig: config.get<string>('pathToConfig'), + pathToJest: config.get<string>('pathToJest'), + restartJestOnSnapshotUpdate: config.get<boolean>('restartJestOnSnapshotUpdate'), + rootPath: path.join(uri.fsPath, config.get<string>('rootPath')), + runAllTestsFirst: config.get<boolean>('runAllTestsFirst'), + showCoverageOnLoad: config.get<boolean>('showCoverageOnLoad'), + coverageFormatter: config.get<string>('coverageFormatter'), + debugMode: config.get<boolean>('debugMode'), + } +} + export class ExtensionManager { debugCodeLensProvider: DebugCodeLensProvider debugConfigurationProvider: DebugConfigurationProvider private extByWorkspace: Map<string, JestExt> = new Map() private context: vscode.ExtensionContext - private commonPluginSettings: IPluginWindowSettings + private commonPluginSettings: PluginWindowSettings constructor(context: vscode.ExtensionContext) { this.context = context @@ -28,7 +57,7 @@ export class ExtensionManager { this.applySettings(getExtensionWindowSettings()) this.registerAll() } - applySettings(settings: IPluginWindowSettings) { + applySettings(settings: PluginWindowSettings) { this.commonPluginSettings = settings const { debugCodeLens } = settings this.debugCodeLensProvider.showWhenTestStateIn = debugCodeLens.enabled ? debugCodeLens.showWhenTestStateIn : [] @@ -176,32 +205,3 @@ export class ExtensionManager { } } } - -export function getExtensionResourceSettings(uri: vscode.Uri): IPluginResourceSettings { - const config = vscode.workspace.getConfiguration('jest', uri) - return { - autoEnable: config.get<boolean>('autoEnable'), - enableInlineErrorMessages: config.get<boolean>('enableInlineErrorMessages'), - enableSnapshotUpdateMessages: config.get<boolean>('enableSnapshotUpdateMessages'), - pathToConfig: config.get<string>('pathToConfig'), - pathToJest: config.get<string>('pathToJest'), - restartJestOnSnapshotUpdate: config.get<boolean>('restartJestOnSnapshotUpdate'), - rootPath: path.join(uri.fsPath, config.get<string>('rootPath')), - runAllTestsFirst: config.get<boolean>('runAllTestsFirst'), - showCoverageOnLoad: config.get<boolean>('showCoverageOnLoad'), - coverageFormatter: config.get<string>('coverageFormatter'), - debugMode: config.get<boolean>('debugMode'), - } -} - -export function getExtensionWindowSettings(): IPluginWindowSettings { - const config = vscode.workspace.getConfiguration('jest') - return { - debugCodeLens: { - enabled: config.get<boolean>('enableCodeLens'), - showWhenTestStateIn: config.get<TestState[]>('debugCodeLens.showWhenTestStateIn'), - }, - enableSnapshotPreviews: config.get<boolean>('enableSnapshotPreviews'), - disabledWorkspaceFolders: config.get<string[]>('disabledWorkspaceFolders'), - } -} diff --git a/src/helpers.ts b/src/helpers.ts index d7731795c..0860cb37e 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -2,7 +2,7 @@ import { platform } from 'os' import { existsSync, readFileSync } from 'fs' import { normalize, join } from 'path' -import { IPluginResourceSettings, hasUserSetPathToJest } from './Settings' +import { PluginResourceSettings, hasUserSetPathToJest } from './Settings' /** * Known binary names of `react-scripts` forks @@ -70,7 +70,7 @@ function isBootstrappedWithCreateReactApp(rootPath: string): boolean { * @returns {string} */ // tslint:disable-next-line no-shadowed-variable -export function pathToJest({ pathToJest, rootPath }: IPluginResourceSettings) { +export function pathToJest({ pathToJest, rootPath }: PluginResourceSettings) { if (hasUserSetPathToJest(pathToJest)) { return normalize(pathToJest) } @@ -88,7 +88,7 @@ export function pathToJest({ pathToJest, rootPath }: IPluginResourceSettings) { * * @returns {string} */ -export function pathToConfig(pluginSettings: IPluginResourceSettings) { +export function pathToConfig(pluginSettings: PluginResourceSettings) { if (pluginSettings.pathToConfig !== '') { return normalize(pluginSettings.pathToConfig) } @@ -107,5 +107,6 @@ export function escapeRegExp(str: string): string { * ANSI colors/characters cleaning based on http://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings */ export function cleanAnsi(str: string): string { + // eslint-disable-next-line no-control-regex return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '') } diff --git a/src/messaging.ts b/src/messaging.ts index 0478939ee..53f154f8c 100644 --- a/src/messaging.ts +++ b/src/messaging.ts @@ -4,26 +4,8 @@ import * as vscode from 'vscode' -export interface MessageAction { - title: string - action: () => void -} - -export function systemErrorMessage(message: string, ...actions: MessageAction[]) { - vscode.window.showErrorMessage(message, ..._extractActionTitles(actions)).then(_handleMessageActions(actions)) -} - -export function systemWarningMessage(message: string, ...actions: MessageAction[]) { - vscode.window.showWarningMessage(message, ..._extractActionTitles(actions)).then(_handleMessageActions(actions)) -} - -// common actions -export const showTroubleshootingAction: MessageAction = { - title: 'Help', - action: () => vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(TROUBLESHOOTING_URL)), -} - export const TROUBLESHOOTING_URL = 'https://github.com/jest-community/vscode-jest/blob/master/README.md#troubleshooting' + // // internal methods // @@ -44,3 +26,22 @@ export function _handleMessageActions(actions?: MessageAction[]): (action?: stri } } } + +export interface MessageAction { + title: string + action: () => void +} + +export function systemErrorMessage(message: string, ...actions: MessageAction[]) { + vscode.window.showErrorMessage(message, ..._extractActionTitles(actions)).then(_handleMessageActions(actions)) +} + +export function systemWarningMessage(message: string, ...actions: MessageAction[]) { + vscode.window.showWarningMessage(message, ..._extractActionTitles(actions)).then(_handleMessageActions(actions)) +} + +// common actions +export const showTroubleshootingAction: MessageAction = { + title: 'Help', + action: () => vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(TROUBLESHOOTING_URL)), +} diff --git a/tests/.eslintrc.js b/tests/.eslintrc.js new file mode 100644 index 000000000..534e61174 --- /dev/null +++ b/tests/.eslintrc.js @@ -0,0 +1,8 @@ +// eslint override for tests only +module.exports = { + rules: { + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + }, +} diff --git a/tests/Coverage/CoverageCodeLensProvider.test.ts b/tests/Coverage/CoverageCodeLensProvider.test.ts index 3c3375e9b..5b6974c0f 100644 --- a/tests/Coverage/CoverageCodeLensProvider.test.ts +++ b/tests/Coverage/CoverageCodeLensProvider.test.ts @@ -32,7 +32,7 @@ jest.mock('vscode', () => { end: Position constructor(start, end) { - rangeConstructor(...arguments) + rangeConstructor() this.start = start this.end = end } diff --git a/tests/Coverage/CoverageMapProvider.test.ts b/tests/Coverage/CoverageMapProvider.test.ts index 46294b1d7..1829c1b17 100644 --- a/tests/Coverage/CoverageMapProvider.test.ts +++ b/tests/Coverage/CoverageMapProvider.test.ts @@ -32,6 +32,7 @@ describe('CoverageMapProvider', () => { it('should be a read-only property', () => { const sut = new CoverageMapProvider() + // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore: Writing to readonly property expect(() => (sut.map = {} as any)).toThrow(TypeError) }) diff --git a/tests/Coverage/Formatters/GutterFormatter.test.ts b/tests/Coverage/Formatters/GutterFormatter.test.ts index 59d80ff18..7fba2d9fe 100644 --- a/tests/Coverage/Formatters/GutterFormatter.test.ts +++ b/tests/Coverage/Formatters/GutterFormatter.test.ts @@ -141,7 +141,7 @@ describe('GutterFormatter', () => { expect(actual.partiallyCovered).toEqual([]) }) - it('should reindex the line numbers', () => { + it('should reindex the line numbers for partially covered', () => { ;(isValidLocation as jest.Mock<any>).mockReturnValueOnce(true) const coverageMapProvider: any = {} @@ -226,7 +226,7 @@ describe('GutterFormatter', () => { expect(actual.partiallyCovered).toEqual([new vscode.Range(1, 2, 5, 3)]) }) - it('should reindex the line numbers', () => { + it('should reindex the line numbers for uncovered', () => { const coverageMapProvider: any = {} const editor: any = { setDecorations: () => ({}), diff --git a/tests/Coverage/index.test.ts b/tests/Coverage/index.test.ts index d19337bb0..37c7b561c 100644 --- a/tests/Coverage/index.test.ts +++ b/tests/Coverage/index.test.ts @@ -1,9 +1,10 @@ jest.disableAutomock() +import * as Coverage from '../../src/Coverage' describe('Coverage', () => { describe('module.exports', () => { it('should match the snapshot', () => { - expect(require('../../src/Coverage')).toMatchSnapshot() + expect(Coverage).toMatchSnapshot() }) }) }) diff --git a/tests/DebugCodeLens/DebugCodeLensProvider.test.ts b/tests/DebugCodeLens/DebugCodeLensProvider.test.ts index 4f08a7336..ada5f1b2e 100644 --- a/tests/DebugCodeLens/DebugCodeLensProvider.test.ts +++ b/tests/DebugCodeLens/DebugCodeLensProvider.test.ts @@ -33,7 +33,7 @@ jest.mock('vscode', () => { end: Position constructor(start, end) { - rangeConstructor(...arguments) + rangeConstructor() this.start = start this.end = end } @@ -275,7 +275,7 @@ describe('DebugCodeLensProvider', () => { }) }) - describe('didChange()', () => { + it('didChange()', () => { const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) sut.onDidChange.fire = jest.fn() sut.didChange() diff --git a/tests/JestExt.test.ts b/tests/JestExt.test.ts index 3b919b38d..b190130cb 100644 --- a/tests/JestExt.test.ts +++ b/tests/JestExt.test.ts @@ -27,6 +27,8 @@ import { updateCurrentDiagnostics } from '../src/diagnostics' import { JestProcessManager, JestProcess } from '../src/JestProcessManagement' import * as messaging from '../src/messaging' +/* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "expectItTakesNoAction"] }] */ + describe('JestExt', () => { const getConfiguration = workspace.getConfiguration as jest.Mock<any> const workspaceFolder = { name: 'test-folder' } as any @@ -39,9 +41,7 @@ describe('JestExt', () => { prepareTestRun: jest.fn(), } as any - // tslint:disable-next-line no-console console.error = jest.fn() - // tslint:disable-next-line no-console console.warn = jest.fn() beforeEach(() => { @@ -158,7 +158,6 @@ describe('JestExt', () => { }) }) - // tslint:disable no-shadowed-variable describe('runTest()', () => { const workspaceFolder = {} as any const fileName = 'fileName' @@ -225,7 +224,7 @@ describe('JestExt', () => { it('should remove the cached decorations', () => { sut.onDidCloseTextDocument(document) - expect(sut.removeCachedDecorationTypes) + expect(sut.removeCachedDecorationTypes).toBeCalled() }) }) @@ -299,10 +298,9 @@ describe('JestExt', () => { }) describe('onDidChangeActiveTextEditor()', () => { - let sut const editor: any = {} const projectWorkspace = new ProjectWorkspace(null, null, null, null) - sut = new JestExt( + const sut = new JestExt( null, workspaceFolder, projectWorkspace, diff --git a/tests/JestProcessManagement/JestProcess.test.ts b/tests/JestProcessManagement/JestProcess.test.ts index 131c7fa20..8f669a22e 100644 --- a/tests/JestProcessManagement/JestProcess.test.ts +++ b/tests/JestProcessManagement/JestProcess.test.ts @@ -226,16 +226,14 @@ describe('JestProcess', () => { expect.assertions(1) const promise = jestProcess.stop() eventEmitter.emit('debuggerProcessExit') - await promise - expect(promise).resolves.toBeUndefined() + await expect(promise).resolves.toBeUndefined() }) it('resolves promise by timeout', async () => { expect.assertions(1) const promise = jestProcess.stop() jest.runAllTimers() - await promise - expect(promise).resolves.toBeUndefined() + await expect(promise).resolves.toBeUndefined() }) it('do not hangs on multiple stop() calls', async () => { @@ -243,8 +241,7 @@ describe('JestProcess', () => { const promise = jestProcess.stop() jestProcess.stop() jest.runAllTimers() - await promise - expect(promise).resolves.toBeUndefined() + await expect(promise).resolves.toBeUndefined() }) }) diff --git a/tests/StatusBar.test.ts b/tests/StatusBar.test.ts index 86fe1d92f..e5f4879cb 100644 --- a/tests/StatusBar.test.ts +++ b/tests/StatusBar.test.ts @@ -1,6 +1,7 @@ jest.unmock('../src/StatusBar') jest.useFakeTimers() +/* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "assertRender"] }] */ const newStatusBarItem = () => ({ text: '', command: '', diff --git a/tests/TestResults/TestResult.test.ts b/tests/TestResults/TestResult.test.ts index 5acb71f6c..2c09efb83 100644 --- a/tests/TestResults/TestResult.test.ts +++ b/tests/TestResults/TestResult.test.ts @@ -2,14 +2,16 @@ jest.unmock('../../src/TestResults/TestResult') jest.unmock('../../src/helpers') jest.mock('path', () => ({ sep: require.requireActual('path').sep })) -import { +import * as TestResult from '../../src/TestResults/TestResult' +import * as path from 'path' + +const { resultsWithLowerCaseWindowsDriveLetters, coverageMapWithLowerCaseWindowsDriveLetters, testResultsWithLowerCaseWindowsDriveLetters, resultsWithoutAnsiEscapeSequence, withLowerCaseWindowsDriveLetter, -} from '../../src/TestResults/TestResult' -import * as path from 'path' +} = TestResult describe('TestResult', () => { describe('resultsWithLowerCaseWindowsDriveLetters', () => { @@ -25,40 +27,21 @@ describe('TestResult', () => { // tslint:disable no-shadowed-variable describe('on Windows systems', () => { beforeEach(() => { - jest.doMock('../../src/TestResults/TestResult', () => ({ - ...require.requireActual('../../src/TestResults/TestResult'), - coverageMapWithLowerCaseWindowsDriveLetters: jest.fn(), - testResultsWithLowerCaseWindowsDriveLetters: jest.fn(), - })) ;(path as any).sep = '\\' }) it('should normalize paths in the coverage map', () => { - const { - resultsWithLowerCaseWindowsDriveLetters, - coverageMapWithLowerCaseWindowsDriveLetters, - } = require('../../src/TestResults/TestResult') - const expected = {} - coverageMapWithLowerCaseWindowsDriveLetters.mockReturnValueOnce(expected) - + const spy = jest.spyOn(TestResult, 'coverageMapWithLowerCaseWindowsDriveLetters') const data: any = { coverageMap: {} } - expect(resultsWithLowerCaseWindowsDriveLetters(data)).toEqual({ - coverageMap: expected, - }) + resultsWithLowerCaseWindowsDriveLetters(data) + expect(spy).toHaveBeenCalled() }) it('should normalize paths in the test results', () => { - const { - resultsWithLowerCaseWindowsDriveLetters, - testResultsWithLowerCaseWindowsDriveLetters, - } = require('../../src/TestResults/TestResult') - const expected = {} - testResultsWithLowerCaseWindowsDriveLetters.mockReturnValueOnce(expected) - + const spy = jest.spyOn(TestResult, 'testResultsWithLowerCaseWindowsDriveLetters') const data: any = { coverageMap: {} } - expect(resultsWithLowerCaseWindowsDriveLetters(data)).toEqual({ - coverageMap: expected, - }) + resultsWithLowerCaseWindowsDriveLetters(data) + expect(spy).toHaveBeenCalled() }) }) }) diff --git a/tests/TestResults/index.test.ts b/tests/TestResults/index.test.ts index 4efc83e15..a58d9eeb8 100644 --- a/tests/TestResults/index.test.ts +++ b/tests/TestResults/index.test.ts @@ -1,9 +1,10 @@ jest.disableAutomock() +import * as tr from '../../src/TestResults' describe('TestResults', () => { describe('module.exports', () => { it('should match the snapshot', () => { - expect(require('../../src/TestResults')).toMatchSnapshot() + expect(tr).toMatchSnapshot() }) }) }) diff --git a/tests/decorations.test.ts b/tests/decorations.test.ts index cc1dd52d2..978452060 100644 --- a/tests/decorations.test.ts +++ b/tests/decorations.test.ts @@ -14,6 +14,17 @@ jest.mock('vscode', () => { import { passingItName, failingItName, skipItName, notRanItName } from '../src/decorations' import * as vscode from 'vscode' +function testRangeBehavior(factoryMethod: () => void) { + it('should set the range behavior', () => { + const mock = (vscode.window.createTextEditorDecorationType as unknown) as jest.Mock<{}> + mock.mockReset() + factoryMethod() + + expect(mock.mock.calls).toHaveLength(1) + expect(mock.mock.calls[0][0].rangeBehavior).toBe(vscode.DecorationRangeBehavior.ClosedClosed) + }) +} + describe('Test Result Annotations', () => { describe('Pass', () => { testRangeBehavior(passingItName) @@ -31,14 +42,3 @@ describe('Test Result Annotations', () => { testRangeBehavior(notRanItName) }) }) - -function testRangeBehavior(factoryMethod: () => void) { - it('should set the range behavior', () => { - const mock = (vscode.window.createTextEditorDecorationType as unknown) as jest.Mock<{}> - mock.mockReset() - factoryMethod() - - expect(mock.mock.calls).toHaveLength(1) - expect(mock.mock.calls[0][0].rangeBehavior).toBe(vscode.DecorationRangeBehavior.ClosedClosed) - }) -} diff --git a/tests/extensionManager.test.ts b/tests/extensionManager.test.ts index 4e1f4c51b..dc318e622 100644 --- a/tests/extensionManager.test.ts +++ b/tests/extensionManager.test.ts @@ -15,7 +15,7 @@ import * as vscode from 'vscode' import { ExtensionManager, getExtensionResourceSettings, getExtensionWindowSettings } from '../src/extensionManager' import { TestState } from '../src/DebugCodeLens' import { readFileSync } from 'fs' -import { IPluginWindowSettings } from '../src/Settings' +import { PluginWindowSettings } from '../src/Settings' vscode.workspace.getConfiguration = jest.fn().mockImplementation((section) => { const data = readFileSync('./package.json') @@ -62,7 +62,7 @@ describe('InstancesManager', () => { describe('applySettings()', () => { it('should save settings to instance', () => { - const newSettings: IPluginWindowSettings = { + const newSettings: PluginWindowSettings = { debugCodeLens: { enabled: true, showWhenTestStateIn: [], @@ -73,7 +73,7 @@ describe('InstancesManager', () => { expect((extensionManager as any).commonPluginSettings).toEqual(newSettings) }) it('should update debugCodeLensProvider instance', () => { - const newSettings: IPluginWindowSettings = { + const newSettings: PluginWindowSettings = { debugCodeLens: { enabled: true, showWhenTestStateIn: [TestState.Fail], @@ -92,7 +92,7 @@ describe('InstancesManager', () => { expect(extensionManager.getByName('workspaceFolder1')).toBeDefined() expect(extensionManager.getByName('workspaceFolder2')).toBeDefined() - const newSettings: IPluginWindowSettings = { + const newSettings: PluginWindowSettings = { debugCodeLens: { enabled: true, showWhenTestStateIn: [], diff --git a/tests/helpers.test.ts b/tests/helpers.test.ts index ca88fadc2..c04a65707 100644 --- a/tests/helpers.test.ts +++ b/tests/helpers.test.ts @@ -126,7 +126,7 @@ describe('ModuleHelpers', () => { mockJoin.mockImplementation(require.requireActual('path').posix.join) mockNormalize.mockImplementation((arg) => arg) - mockExistsSync.mockImplementation((_) => false) + mockExistsSync.mockImplementation(() => false) expect(pathToJest(defaultSettings)).toBe(expected) }) diff --git a/tests/messaging.test.ts b/tests/messaging.test.ts index 4505b4aff..e760af586 100644 --- a/tests/messaging.test.ts +++ b/tests/messaging.test.ts @@ -10,7 +10,9 @@ describe('test system messaging', () => { const mockExecCommands = commands.executeCommand as jest.Mock<any> const mockUriParse = Uri.parse as jest.Mock<any> - const thenable = () => {} + const thenable = () => { + /* do nothing */ + } beforeEach(() => { jest.resetAllMocks() diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 2ff011a8d..000000000 --- a/tslint.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "extends": ["tslint:latest", "tslint-config-prettier"], - "rules": { - "class-name": true, - "curly": true, - "eofline": true, - "no-duplicate-variable": true, - "no-var-keyword": true, - "prefer-const": true, - "triple-equals": true, - "no-unused-expression": [true, "allow-new"], - "variable-name": [true, "ban-keywords", "check-format", "allow-leading-underscore"], - "object-literal-sort-keys": false, - "ordered-imports": false, - "member-access": false, - "interface-name": false, - "no-empty": [true, "allow-empty-catch", "allow-empty-functions"], - "no-this-assignment": [true, { "allow-destructuring": true }], - "no-implicit-dependencies": [true, ["vscode"]], - "array-type": [true, "array-simple"] - } -} diff --git a/yarn.lock b/yarn.lock index c0d12dd77..7e0c6595a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -648,6 +648,11 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/eslint-visitor-keys@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" + integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== + "@types/graceful-fs@^4.1.2": version "4.1.3" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f" @@ -683,6 +688,11 @@ jest-diff "^25.2.1" pretty-format "^25.2.1" +"@types/json-schema@^7.0.3": + version "7.0.4" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" + integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== + "@types/node@*", "@types/node@>= 8": version "13.13.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.4.tgz#1581d6c16e3d4803eb079c87d4ac893ee7501c2c" @@ -737,6 +747,49 @@ dependencies: "@types/yargs-parser" "*" +"@typescript-eslint/eslint-plugin@^2.32.0": + version "2.32.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.32.0.tgz#5d5cc2e00b1d4a4b848cc68bfdd3aede1ef0ad16" + integrity sha512-nb1kSUa8cd22hGgxpGdVT6/iyP7IKyrnyZEGYo+tN8iyDdXvXa+nfsX03tJVeFfhbkwR/0CDk910zPbqSflAsg== + dependencies: + "@typescript-eslint/experimental-utils" "2.32.0" + functional-red-black-tree "^1.0.1" + regexpp "^3.0.0" + tsutils "^3.17.1" + +"@typescript-eslint/experimental-utils@2.32.0", "@typescript-eslint/experimental-utils@^2.5.0": + version "2.32.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.32.0.tgz#bee7fbe1d21d13a273066d70abc82549d0b7943e" + integrity sha512-oDWuB2q5AXsQ/mLq2N4qtWiBASWXPf7KhqXgeGH4QsyVKx+km8F6Vfqd3bspJQyhyCqxcbLO/jKJuIV3DzHZ6A== + dependencies: + "@types/json-schema" "^7.0.3" + "@typescript-eslint/typescript-estree" "2.32.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + +"@typescript-eslint/parser@^2.32.0": + version "2.32.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.32.0.tgz#a1ace8ab1af529580bfb6cc2cd55fd8d8b1e68ab" + integrity sha512-swRtH835fUfm2khchiOVNchU3gVNaZNj2pY92QSx4kXan+RzaGNrwIRaCyX8uqzmK0xNPzseaUYHP8CsmrsjFw== + dependencies: + "@types/eslint-visitor-keys" "^1.0.0" + "@typescript-eslint/experimental-utils" "2.32.0" + "@typescript-eslint/typescript-estree" "2.32.0" + eslint-visitor-keys "^1.1.0" + +"@typescript-eslint/typescript-estree@2.32.0": + version "2.32.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.32.0.tgz#0e4ae2e883557f94039b13ac0ecfcfbb09835b8d" + integrity sha512-hQpbWM/Y2iq6jB9FHYJBqa3h1R9IEGodOtajhb261cVHt9cz30AKjXM6WP7LxJdEPPlyJ9rPTZVgBUgZgiyPgw== + dependencies: + debug "^4.1.1" + eslint-visitor-keys "^1.1.0" + glob "^7.1.6" + is-glob "^4.0.1" + lodash "^4.17.15" + semver "^7.3.2" + tsutils "^3.17.1" + "@webassemblyjs/ast@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" @@ -912,6 +965,11 @@ acorn-globals@^4.3.2: acorn "^6.0.1" acorn-walk "^6.0.1" +acorn-jsx@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" + integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== + acorn-walk@^6.0.1: version "6.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" @@ -927,6 +985,11 @@ acorn@^7.1.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== +acorn@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" + integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== + agent-base@4, agent-base@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" @@ -952,7 +1015,7 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da" integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ== -ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.5: +ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: version "6.12.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== @@ -1052,11 +1115,28 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= +array-includes@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.1.tgz#cdd67e6852bdf9c1215460786732255ed2459348" + integrity sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0" + is-string "^1.0.5" + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array.prototype.flat@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz#0de82b426b0318dbfdb940089e38b043d37f6c7b" + integrity sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -1413,11 +1493,6 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= - builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" @@ -1506,6 +1581,11 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -1580,6 +1660,11 @@ cli-truncate@^2.1.0: slice-ansi "^3.0.0" string-width "^4.2.0" +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -1657,7 +1742,7 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.12.1, commander@^2.18.0, commander@^2.20.0: +commander@^2.18.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -1667,6 +1752,11 @@ commander@^5.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== +comment-parser@^0.7.4: + version "0.7.4" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.4.tgz#f5eb83cbae323cae6533c057f41d52692361c83a" + integrity sha512-Nnl77/mt6sj1BiYSVMeMWzvD0183F2MFOJyFRmZHimUVDYS9J40AvXpiFA7RpU5pQH+HkvYc0dnsHpwW2xmbyQ== + commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" @@ -1702,6 +1792,11 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + integrity sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo= + convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" @@ -1805,7 +1900,7 @@ cross-spawn@6.0.5, cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.2" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.2.tgz#d0d7dcfa74e89115c7619f4f721a94e1fdb716d6" integrity sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw== @@ -1918,7 +2013,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@^2.2.0, debug@^2.3.3: +debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -1932,7 +2027,7 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== @@ -1954,7 +2049,7 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -deep-is@~0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= @@ -2038,11 +2133,6 @@ diff-sequences@^25.2.6: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd" integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg== -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -2052,6 +2142,21 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + integrity sha1-N53Ocw9hZvds76TmcHoVmwLFpvo= + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + domain-browser@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" @@ -2166,14 +2271,14 @@ errno@^0.1.3, errno@~0.1.7: dependencies: prr "~1.0.1" -error-ex@^1.3.1: +error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: +es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.5: version "1.17.5" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9" integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg== @@ -2228,6 +2333,72 @@ escodegen@^1.11.1: optionalDependencies: source-map "~0.6.1" +eslint-config-prettier@^6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" + integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== + dependencies: + get-stdin "^6.0.0" + +eslint-import-resolver-node@^0.3.2: + version "0.3.3" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404" + integrity sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg== + dependencies: + debug "^2.6.9" + resolve "^1.13.1" + +eslint-module-utils@^2.4.1: + version "2.6.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz#579ebd094f56af7797d19c9866c9c9486629bfa6" + integrity sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA== + dependencies: + debug "^2.6.9" + pkg-dir "^2.0.0" + +eslint-plugin-import@^2.20.2: + version "2.20.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.20.2.tgz#91fc3807ce08be4837141272c8b99073906e588d" + integrity sha512-FObidqpXrR8OnCh4iNsxy+WACztJLXAHBO5hK79T1Hc77PgQZkyDGA5Ag9xAvRpglvLNxhH/zSmZ70/pZ31dHg== + dependencies: + array-includes "^3.0.3" + array.prototype.flat "^1.2.1" + contains-path "^0.1.0" + debug "^2.6.9" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.2" + eslint-module-utils "^2.4.1" + has "^1.0.3" + minimatch "^3.0.4" + object.values "^1.1.0" + read-pkg-up "^2.0.0" + resolve "^1.12.0" + +eslint-plugin-jest@^23.10.0: + version "23.10.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-23.10.0.tgz#4738c7ca9e6513da50f4e99d7b161c1f82fa8e8f" + integrity sha512-cHC//nesojSO1MLxVmFJR/bUaQQG7xvMHQD8YLbsQzevR41WKm8paKDUv2wMHlUy5XLZUmNcWuflOi4apS8D+Q== + dependencies: + "@typescript-eslint/experimental-utils" "^2.5.0" + +eslint-plugin-jsdoc@^25.2.0: + version "25.2.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-25.2.0.tgz#dd90f0a11778888637ea3aaaae1a6d3bfa3f3132" + integrity sha512-P+3zIyu5fSEZlAtoKI+pGxO8XWwWG7RlO7Zm25+sZTcWUb4jGteYSYLt04ChMauHjoDJSzhwfTXhAn2gVUijpQ== + dependencies: + comment-parser "^0.7.4" + debug "^4.1.1" + jsdoctypeparser "^6.1.0" + lodash "^4.17.15" + regextras "^0.7.0" + semver "^6.3.0" + spdx-expression-parse "^3.0.0" + +eslint-plugin-prefer-arrow@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.1.tgz#9e2943cdae4476e41f94f50dd7a250f267db6865" + integrity sha512-CPAvdTGG0YbFAJrUKdRBrOJ0X1I7jTtF5VIM4m2Bw1/A2jrhfUeUAcPy4pAEB5DNaUuDqc59f3pKTeiVeamS1A== + eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" @@ -2236,11 +2407,89 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" + integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + +eslint@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.0.0.tgz#c35dfd04a4372110bd78c69a8d79864273919a08" + integrity sha512-qY1cwdOxMONHJfGqw52UOpZDeqXy8xmD0u8CT6jIstil72jkhURC704W8CFyTPDPllz4z4lu0Ql1+07PG/XdIg== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + eslint-visitor-keys "^1.1.0" + espree "^7.0.0" + esquery "^1.2.0" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash "^4.17.14" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.0.0.tgz#8a7a60f218e69f120a842dc24c5a88aa7748a74e" + integrity sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +esquery@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" @@ -2253,6 +2502,11 @@ estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estraverse@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" + integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -2394,6 +2648,15 @@ extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -2433,7 +2696,7 @@ fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -2450,13 +2713,20 @@ figgy-pudding@^3.5.1: resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== -figures@^3.2.0: +figures@^3.0.0, figures@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== dependencies: escape-string-regexp "^1.0.5" +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -2488,6 +2758,13 @@ find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -2513,6 +2790,20 @@ findup-sync@3.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -2602,16 +2893,16 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== -get-caller-file@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -2683,7 +2974,14 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: +glob-parent@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -2736,6 +3034,13 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" @@ -2917,7 +3222,7 @@ hyperlinker@^1.0.0: resolved "https://registry.yarnpkg.com/hyperlinker/-/hyperlinker-1.0.0.tgz#23dc9e38a206b208ee49bc2d6c8ef47027df0c0e" integrity sha512-Ty8UblRWFEcfSuIaajM34LdPXIhbs1ajEX/BBPv24J+enSVaEVY63xQ6lTO9VRYS5LAoghIG0IDJ+p+IPzKUQQ== -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -2934,7 +3239,12 @@ iferr@^0.1.5: resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= -import-fresh@^3.1.0: +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +import-fresh@^3.0.0, import-fresh@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== @@ -3001,6 +3311,25 @@ ini@^1.3.4, ini@^1.3.5: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== +inquirer@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" + integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^3.0.0" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + interpret@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" @@ -3140,7 +3469,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0: +is-glob@^4.0.0, is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -3200,6 +3529,11 @@ is-stream@^2.0.0: resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3" integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw== +is-string@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" + integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== + is-symbol@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" @@ -3789,6 +4123,11 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +jsdoctypeparser@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jsdoctypeparser/-/jsdoctypeparser-6.1.0.tgz#acfb936c26300d98f1405cb03e20b06748e512a8" + integrity sha512-UCQBZ3xCUBv/PLfwKAJhp6jmGOSLFNKzrotXGNgbKhWvz27wPsCsVeP7gIcHPElQw2agBmynAitXqhxR58XAmA== + jsdom@^15.2.1: version "15.2.1" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-15.2.1.tgz#d2feb1aef7183f86be521b8c6833ff5296d07ec5" @@ -3841,6 +4180,11 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -3967,6 +4311,14 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -4024,6 +4376,16 @@ listr2@1.3.8: through "^2.3.8" uuid "^7.0.2" +load-json-file@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" + integrity sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg= + dependencies: + graceful-fs "^4.1.2" + parse-json "^2.2.0" + pify "^2.0.0" + strip-bom "^3.0.0" + loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" @@ -4047,6 +4409,14 @@ loader-utils@^1.0.2, loader-utils@^1.2.3: emojis-list "^3.0.0" json5 "^1.0.1" +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -4142,7 +4512,7 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= -lodash@^4.17.13, lodash@^4.17.15: +lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -4390,14 +4760,6 @@ mkdirp@^0.5.1, mkdirp@^0.5.3: dependencies: minimist "^1.2.5" -mock-require@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/mock-require/-/mock-require-3.0.3.tgz#ccd544d9eae81dd576b3f219f69ec867318a1946" - integrity sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg== - dependencies: - get-caller-file "^1.0.2" - normalize-path "^2.1.1" - move-concurrently@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" @@ -4420,6 +4782,11 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + nan@^2.12.1: version "2.14.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01" @@ -4517,7 +4884,7 @@ node-notifier@^6.0.0: shellwords "^0.1.1" which "^1.3.1" -normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -4619,6 +4986,16 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +object.values@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" + integrity sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.0-next.1" + function-bind "^1.1.1" + has "^1.0.3" + octokit-pagination-methods@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz#cf472edc9d551055f9ef73f6e42b4dbb4c80bea4" @@ -4650,6 +5027,18 @@ optionator@^0.8.1: type-check "~0.3.2" word-wrap "~1.2.3" +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" @@ -4672,6 +5061,11 @@ os-name@^3.1.0: macos-release "^2.2.0" windows-release "^3.1.0" +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + override-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/override-require/-/override-require-1.1.1.tgz#6ae22fadeb1f850ffb0cf4c20ff7b87e5eb650df" @@ -4702,6 +5096,13 @@ p-is-promise@^2.0.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e" integrity sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg== +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + p-limit@^2.0.0, p-limit@^2.1.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -4709,6 +5110,13 @@ p-limit@^2.0.0, p-limit@^2.1.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -4730,6 +5138,11 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -4794,6 +5207,13 @@ parse-github-url@^1.0.2: resolved "https://registry.yarnpkg.com/parse-github-url/-/parse-github-url-1.0.2.tgz#242d3b65cbcdda14bb50439e3242acf6971db395" integrity sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw== +parse-json@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" + integrity sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= + dependencies: + error-ex "^1.2.0" + parse-json@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.0.0.tgz#73e5114c986d143efa3712d4ea24db9a4266f60f" @@ -4866,6 +5286,13 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== +path-type@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" + integrity sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM= + dependencies: + pify "^2.0.0" + path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -4892,6 +5319,11 @@ picomatch@^2.0.4, picomatch@^2.0.5: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -4909,6 +5341,13 @@ pirates@^4.0.1: dependencies: node-modules-regexp "^1.0.0" +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -4940,6 +5379,11 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -4988,6 +5432,11 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -5107,6 +5556,14 @@ react-is@^16.12.0, react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== +read-pkg-up@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" + integrity sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4= + dependencies: + find-up "^2.0.0" + read-pkg "^2.0.0" + read-pkg-up@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" @@ -5116,6 +5573,15 @@ read-pkg-up@^7.0.1: read-pkg "^5.2.0" type-fest "^0.8.1" +read-pkg@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" + integrity sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg= + dependencies: + load-json-file "^2.0.0" + normalize-package-data "^2.3.2" + path-type "^2.0.0" + read-pkg@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" @@ -5187,6 +5653,16 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^3.0.0, regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + +regextras@^0.7.0: + version "0.7.1" + resolved "https://registry.yarnpkg.com/regextras/-/regextras-0.7.1.tgz#be95719d5f43f9ef0b9fa07ad89b7c606995a3b2" + integrity sha512-9YXf6xtW+qzQ+hcMQXx95MOvfqXFgsKDZodX3qZB0x2n5Z94ioetIITsBtvJbiOyxa/6s9AtyweBLCdPmPko/w== + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -5306,7 +5782,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= -resolve@1.x, resolve@^1.10.0, resolve@^1.17.0, resolve@^1.3.2: +resolve@1.x, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.17.0, resolve@^1.3.2: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -5331,6 +5807,13 @@ retry@0.12.0: resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -5358,6 +5841,11 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-queue@^1.0.0, run-queue@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" @@ -5365,7 +5853,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.3.3: +rxjs@^6.3.3, rxjs@^6.5.3: version "6.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== @@ -5430,7 +5918,7 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -5440,6 +5928,11 @@ semver@6.x, semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.2.1, semver@^7.3.2: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + serialize-javascript@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-2.1.2.tgz#ecec53b0e0317bdc95ef76ab7074b7384785fa61" @@ -5522,6 +6015,15 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + slice-ansi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" @@ -5836,6 +6338,11 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= + strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" @@ -5851,6 +6358,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-json-comments@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.0.tgz#7638d31422129ecf4457440009fba03f9f9ac180" + integrity sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w== + supports-color@6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" @@ -5893,6 +6405,16 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -5939,6 +6461,11 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + throat@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" @@ -5952,7 +6479,7 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -through@^2.3.8: +through@^2.3.6, through@^2.3.8: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -5964,6 +6491,13 @@ timers-browserify@^2.0.4: dependencies: setimmediate "^1.0.4" +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" @@ -6063,39 +6597,15 @@ ts-loader@^7.0.1: micromatch "^4.0.0" semver "^6.0.0" -tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.8.1, tslib@^1.9.0: version "1.11.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== -tslint-config-prettier@^1.18.0: - version "1.18.0" - resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37" - integrity sha512-xPw9PgNPLG3iKRxmK7DWr+Ea/SzrvfHtjFt5LBl61gk2UBG/DB9kCXRjv+xyIU1rUtnayLeMUVJBcMX8Z17nDg== - -tslint@^6.1.2: - version "6.1.2" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.2.tgz#2433c248512cc5a7b2ab88ad44a6b1b34c6911cf" - integrity sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA== - dependencies: - "@babel/code-frame" "^7.0.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^4.0.1" - glob "^7.1.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - mkdirp "^0.5.3" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.10.0" - tsutils "^2.29.0" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== +tsutils@^3.17.1: + version "3.17.1" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" + integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== dependencies: tslib "^1.8.1" @@ -6116,6 +6626,13 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -6155,15 +6672,6 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript-tslint-plugin@^0.5.5: - version "0.5.5" - resolved "https://registry.yarnpkg.com/typescript-tslint-plugin/-/typescript-tslint-plugin-0.5.5.tgz#673875c43640251f1ab3d63745d7d49726ff961c" - integrity sha512-tR5igNQP+6FhxaPJYRlUBVsEl0n5cSuXRbg7L1y80mL4B1jUHb8uiIcbQBJ9zWyypJEdFYFUccpXxvMwZR8+AA== - dependencies: - minimatch "^3.0.4" - mock-require "^3.0.3" - vscode-languageserver "^5.2.1" - typescript@^3.4.3, typescript@^3.8.3: version "3.8.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" @@ -6297,6 +6805,11 @@ v8-compile-cache@2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== +v8-compile-cache@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== + v8-to-istanbul@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.3.tgz#22fe35709a64955f49a08a7c7c959f6520ad6f20" @@ -6328,32 +6841,6 @@ vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== -vscode-jsonrpc@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz#a7bf74ef3254d0a0c272fab15c82128e378b3be9" - integrity sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg== - -vscode-languageserver-protocol@3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz#b8aab6afae2849c84a8983d39a1cf742417afe2f" - integrity sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g== - dependencies: - vscode-jsonrpc "^4.0.0" - vscode-languageserver-types "3.14.0" - -vscode-languageserver-types@3.14.0: - version "3.14.0" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" - integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== - -vscode-languageserver@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz#0d2feddd33f92aadf5da32450df498d52f6f14eb" - integrity sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A== - dependencies: - vscode-languageserver-protocol "3.14.1" - vscode-uri "^1.0.6" - vscode-test@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-1.3.0.tgz#3310ab385d9b887b4c82e8f52be1030e7cf9493d" @@ -6363,11 +6850,6 @@ vscode-test@^1.3.0: https-proxy-agent "^2.2.4" rimraf "^2.6.3" -vscode-uri@^1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-1.0.8.tgz#9769aaececae4026fb6e22359cb38946580ded59" - integrity sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ== - w3c-hr-time@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" @@ -6513,7 +6995,7 @@ windows-release@^3.1.0: dependencies: execa "^1.0.0" -word-wrap@~1.2.3: +word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -6558,6 +7040,13 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + ws@^7.0.0: version "7.2.5" resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.5.tgz#abb1370d4626a5a9cd79d8de404aa18b3465d10d" From 170285fcebcd51c0e934ba19fe50314fbd6a9a64 Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Sun, 24 May 2020 14:35:58 -0400 Subject: [PATCH 08/11] remove API snapshot tests --- tests/Coverage/__snapshots__/index.test.ts.snap | 9 --------- tests/Coverage/index.test.ts | 10 ---------- tests/TestResults/__snapshots__/index.test.ts.snap | 14 -------------- tests/TestResults/index.test.ts | 10 ---------- 4 files changed, 43 deletions(-) delete mode 100644 tests/Coverage/__snapshots__/index.test.ts.snap delete mode 100644 tests/Coverage/index.test.ts delete mode 100644 tests/TestResults/__snapshots__/index.test.ts.snap delete mode 100644 tests/TestResults/index.test.ts diff --git a/tests/Coverage/__snapshots__/index.test.ts.snap b/tests/Coverage/__snapshots__/index.test.ts.snap deleted file mode 100644 index d3efbbe11..000000000 --- a/tests/Coverage/__snapshots__/index.test.ts.snap +++ /dev/null @@ -1,9 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Coverage module.exports should match the snapshot 1`] = ` -Object { - "CoverageCodeLensProvider": [Function], - "CoverageMapProvider": [Function], - "CoverageOverlay": [Function], -} -`; diff --git a/tests/Coverage/index.test.ts b/tests/Coverage/index.test.ts deleted file mode 100644 index 37c7b561c..000000000 --- a/tests/Coverage/index.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -jest.disableAutomock() -import * as Coverage from '../../src/Coverage' - -describe('Coverage', () => { - describe('module.exports', () => { - it('should match the snapshot', () => { - expect(Coverage).toMatchSnapshot() - }) - }) -}) diff --git a/tests/TestResults/__snapshots__/index.test.ts.snap b/tests/TestResults/__snapshots__/index.test.ts.snap deleted file mode 100644 index 64e6f75c8..000000000 --- a/tests/TestResults/__snapshots__/index.test.ts.snap +++ /dev/null @@ -1,14 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TestResults module.exports should match the snapshot 1`] = ` -Object { - "TestReconciliationState": Object { - "KnownFail": "KnownFail", - "KnownSkip": "KnownSkip", - "KnownSuccess": "KnownSuccess", - "Unknown": "Unknown", - }, - "TestResultProvider": [Function], - "resultsWithLowerCaseWindowsDriveLetters": [Function], -} -`; diff --git a/tests/TestResults/index.test.ts b/tests/TestResults/index.test.ts deleted file mode 100644 index a58d9eeb8..000000000 --- a/tests/TestResults/index.test.ts +++ /dev/null @@ -1,10 +0,0 @@ -jest.disableAutomock() -import * as tr from '../../src/TestResults' - -describe('TestResults', () => { - describe('module.exports', () => { - it('should match the snapshot', () => { - expect(tr).toMatchSnapshot() - }) - }) -}) From 603030ae820c9e86a18030464f2f8e82a188727d Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Sun, 24 May 2020 19:04:50 -0400 Subject: [PATCH 09/11] wrap prettier inside eslint and removed lint-staged --- .eslintrc.js | 18 +-- .vscode/settings.json | 3 +- package.json | 16 +-- prettier.config.js | 2 +- yarn.lock | 304 ++++-------------------------------------- 5 files changed, 41 insertions(+), 302 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 387dce9d2..16f919d4e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,21 +1,21 @@ module.exports = { root: true, env: { - "es6": true, - "node": true, - "jest/globals": true, + es6: true, + node: true, + 'jest/globals': true, }, parser: '@typescript-eslint/parser', - plugins: [ - '@typescript-eslint', - 'jest', - ], + plugins: ['@typescript-eslint', 'jest', 'prettier'], extends: [ 'eslint:recommended', 'plugin:@typescript-eslint/eslint-recommended', 'plugin:@typescript-eslint/recommended', 'plugin:jest/recommended', - 'prettier', 'prettier/@typescript-eslint', + 'plugin:prettier/recommended', ], -}; \ No newline at end of file + rules: { + 'prettier/prettier': 'error', + }, +} diff --git a/.vscode/settings.json b/.vscode/settings.json index cf069b1b4..ac2b5901e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,8 +10,9 @@ }, "typescript.tsdk": "./node_modules/typescript/lib", // eslint + "tslint.enable": false, "eslint.enable": true, "editor.codeActionsOnSave": { "source.fixAll.eslint": true - } + }, } diff --git a/package.json b/package.json index 47548c0b3..79250f062 100644 --- a/package.json +++ b/package.json @@ -266,23 +266,15 @@ } ] }, - "lint-staged": { - "*.json": "yarn prettier --write", - "*.ts": "yarn prettier --write" - }, "scripts": { - "precommit": "lint-staged", - "ci": "yarn lint && yarn prettier-project-check && yarn test --coverage", + "ci": "yarn lint && yarn test --coverage", "clean-out": "rimraf ./out", "vscode:prepublish": "yarn clean-out && yarn compile", "compile": "webpack --mode production", "watch": "webpack --mode development --watch --info-verbosity verbose", - "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"", + "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"*.json\"", "test": "jest", - "watch-test": "yarn test -- --watch", - "prettier": "prettier", - "prettier-project-check": "yarn prettier --check \"?(__mocks__|src|tests)/**/*.ts\" \"*.json\"", - "prettier-project-write": "yarn prettier --write \"?(__mocks__|src|tests)/**/*.ts\" \"*.json\"" + "watch-test": "yarn test -- --watch" }, "dependencies": { "istanbul-lib-coverage": "^1.1.1", @@ -304,8 +296,8 @@ "eslint-plugin-jest": "^23.10.0", "eslint-plugin-jsdoc": "^25.2.0", "eslint-plugin-prefer-arrow": "^1.2.1", + "eslint-plugin-prettier": "^3.1.3", "jest": "^25.5.0", - "lint-staged": "^10.2.0", "prettier": "^2.0.5", "rimraf": "^3.0.2", "ts-jest": "^25.4.0", diff --git a/prettier.config.js b/prettier.config.js index ba0e3dee8..575644bb2 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -11,4 +11,4 @@ module.exports = { }, }, ], -}; +} diff --git a/yarn.lock b/yarn.lock index 7e0c6595a..44b59df7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -227,13 +227,6 @@ core-js "^2.6.5" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.9.2": - version "7.9.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f" - integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ== - dependencies: - regenerator-runtime "^0.13.4" - "@babel/template@^7.3.3", "@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6": version "7.8.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b" @@ -596,13 +589,6 @@ dependencies: "@types/node" ">= 8" -"@samverschueren/stream-to-observable@^0.3.0": - version "0.3.0" - resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" - integrity sha512-MI4Xx6LHs4Webyvi6EbspgyAb4D2Q2VtnCQ1blOJcoLS6mVa8lNN2rkIy1CVxfTUpoyIbCTkXES1rLXztFD1lg== - dependencies: - any-observable "^0.3.0" - "@sinonjs/commons@^1.7.0": version "1.7.2" resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.7.2.tgz#505f55c74e0272b43f6c52d81946bed7058fc0e2" @@ -708,11 +694,6 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== -"@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== - "@types/prettier@^1.19.0": version "1.19.1" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" @@ -997,14 +978,6 @@ agent-base@4, agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" -aggregate-error@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0" - integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -1025,12 +998,7 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-colors@^3.2.1: - version "3.2.4" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" - integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== - -ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: +ansi-escapes@^4.2.1: version "4.3.1" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== @@ -1062,11 +1030,6 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: "@types/color-name" "^1.1.1" color-convert "^2.0.1" -any-observable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.3.0.tgz#af933475e5806a67d0d7df090dd5e8bef65d119b" - integrity sha512-/FQM1EDkTsf63Ub2C6O7GuYFDsSXUwsaZDurV0np41ocwq0jthUAYCmhBX9f+KwlaCgIuWyr/4WlUQUBfKfZog== - anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1176,11 +1139,6 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -1556,7 +1514,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.2: +chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1640,11 +1598,6 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -1652,14 +1605,6 @@ cli-cursor@^3.1.0: dependencies: restore-cursor "^3.1.0" -cli-truncate@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" @@ -1683,11 +1628,6 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= - co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -1747,11 +1687,6 @@ commander@^2.18.0, commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== - comment-parser@^0.7.4: version "0.7.4" resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-0.7.4.tgz#f5eb83cbae323cae6533c057f41d52692361c83a" @@ -1836,17 +1771,6 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cosmiconfig@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-6.0.0.tgz#da4fee853c52f6b1e6935f41c1a2fc50bd4a9982" - integrity sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg== - dependencies: - "@types/parse-json" "^4.0.0" - import-fresh "^3.1.0" - parse-json "^5.0.0" - path-type "^4.0.0" - yaml "^1.7.2" - coveralls@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-3.1.0.tgz#13c754d5e7a2dd8b44fe5269e21ca394fb4d615b" @@ -2044,11 +1968,6 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= - deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" @@ -2059,13 +1978,6 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== -defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= - dependencies: - clone "^1.0.2" - define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -2194,11 +2106,6 @@ ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer "^5.0.1" -elegant-spinner@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-2.0.0.tgz#f236378985ecd16da75488d166be4b688fd5af94" - integrity sha512-5YRYHhvhYzV/FC4AiMdeSIg3jAYGq9xFvbhZMpPlJoBsfYgrw2DSCYeXfat6tYBu45PWiyRr3+flaCPPmviPaA== - elliptic@^6.0.0: version "6.5.2" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" @@ -2257,13 +2164,6 @@ enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: memory-fs "^0.5.0" tapable "^1.0.0" -enquirer@^2.3.4: - version "2.3.5" - resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.5.tgz#3ab2b838df0a9d8ab9e7dff235b0e8712ef92381" - integrity sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA== - dependencies: - ansi-colors "^3.2.1" - errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -2399,6 +2299,13 @@ eslint-plugin-prefer-arrow@^1.2.1: resolved "https://registry.yarnpkg.com/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.1.tgz#9e2943cdae4476e41f94f50dd7a250f267db6865" integrity sha512-CPAvdTGG0YbFAJrUKdRBrOJ0X1I7jTtF5VIM4m2Bw1/A2jrhfUeUAcPy4pAEB5DNaUuDqc59f3pKTeiVeamS1A== +eslint-plugin-prettier@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca" + integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-scope@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" @@ -2564,21 +2471,6 @@ execa@^3.2.0: signal-exit "^3.0.2" strip-final-newline "^2.0.0" -execa@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-4.0.0.tgz#7f37d6ec17f09e6b8fc53288611695b6d12b9daf" - integrity sha512-JbDUxwV3BoT5ZVXQrSVbAiaXhXUkIwvbhPIwZ0N13kX+5yCzOhUNdocxB/UQRuYOHRYYwAxKYwJYc0T4D12pDA== - dependencies: - cross-spawn "^7.0.0" - get-stream "^5.0.0" - human-signals "^1.1.1" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.0" - onetime "^5.1.0" - signal-exit "^3.0.2" - strip-final-newline "^2.0.0" - exit@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" @@ -2686,6 +2578,11 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-json-patch@^3.0.0-1: version "3.0.0-1" resolved "https://registry.yarnpkg.com/fast-json-patch/-/fast-json-patch-3.0.0-1.tgz#4c68f2e7acfbab6d29d1719c44be51899c93dabb" @@ -2713,7 +2610,7 @@ figgy-pudding@^3.5.1: resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== -figures@^3.0.0, figures@^3.2.0: +figures@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== @@ -2908,11 +2805,6 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-own-enumerable-property-symbols@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" - integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== - get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" @@ -3244,7 +3136,7 @@ ignore@^4.0.6: resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== -import-fresh@^3.0.0, import-fresh@^3.1.0: +import-fresh@^3.0.0: version "3.2.1" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== @@ -3273,11 +3165,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - infer-owner@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -3488,11 +3375,6 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== -is-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" - integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= - is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -3514,11 +3396,6 @@ is-regex@^1.0.5: dependencies: has "^1.0.3" -is-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" - integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= - is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -4337,45 +4214,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -lint-staged@^10.2.0: - version "10.2.2" - resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.2.tgz#901403c120eb5d9443a0358b55038b04c8a7db9b" - integrity sha512-78kNqNdDeKrnqWsexAmkOU3Z5wi+1CsQmUmfCuYgMTE8E4rAIX8RHW7xgxwAZ+LAayb7Cca4uYX4P3LlevzjVg== - dependencies: - chalk "^4.0.0" - commander "^5.0.0" - cosmiconfig "^6.0.0" - debug "^4.1.1" - dedent "^0.7.0" - execa "^4.0.0" - listr2 "1.3.8" - log-symbols "^3.0.0" - micromatch "^4.0.2" - normalize-path "^3.0.0" - please-upgrade-node "^3.2.0" - string-argv "0.3.1" - stringify-object "^3.3.0" - -listr2@1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-1.3.8.tgz#30924d79de1e936d8c40af54b6465cb814a9c828" - integrity sha512-iRDRVTgSDz44tBeBBg/35TQz4W+EZBWsDUq7hPpqeUHm7yLPNll0rkwW3lIX9cPAK7l+x95mGWLpxjqxftNfZA== - dependencies: - "@samverschueren/stream-to-observable" "^0.3.0" - chalk "^3.0.0" - cli-cursor "^3.1.0" - cli-truncate "^2.1.0" - elegant-spinner "^2.0.0" - enquirer "^2.3.4" - figures "^3.2.0" - indent-string "^4.0.0" - log-update "^4.0.0" - p-map "^4.0.0" - pad "^3.2.0" - rxjs "^6.3.3" - through "^2.3.8" - uuid "^7.0.2" - load-json-file@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" @@ -4522,23 +4360,6 @@ log-driver@^1.2.7: resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== -log-symbols@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" - integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== - dependencies: - chalk "^2.4.2" - -log-update@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" - integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== - dependencies: - ansi-escapes "^4.3.0" - cli-cursor "^3.1.0" - slice-ansi "^4.0.0" - wrap-ansi "^6.2.0" - lolex@^5.0.0: version "5.1.2" resolved "https://registry.yarnpkg.com/lolex/-/lolex-5.1.2.tgz#953694d098ce7c07bc5ed6d0e42bc6c0c6d5a367" @@ -5131,13 +4952,6 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -5148,13 +4962,6 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -pad@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/pad/-/pad-3.2.0.tgz#be7a1d1cb6757049b4ad5b70e71977158fea95d1" - integrity sha512-2u0TrjcGbOjBTJpyewEl4hBO3OeX5wWue7eIFPzQTg6wFSvoaHcBTTUY5m+n0hd04gmTCPuY0kCpVIVuw5etwg== - dependencies: - wcwidth "^1.0.1" - pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -5293,11 +5100,6 @@ path-type@^2.0.0: dependencies: pify "^2.0.0" -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -5362,13 +5164,6 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -please-upgrade-node@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" - integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg== - dependencies: - semver-compare "^1.0.0" - pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -5389,6 +5184,13 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" @@ -5853,7 +5655,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.3.3, rxjs@^6.5.3: +rxjs@^6.5.3: version "6.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== @@ -5913,11 +5715,6 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -semver-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" - integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= - "semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -6024,24 +5821,6 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -6236,11 +6015,6 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= -string-argv@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" - integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== - string-length@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" @@ -6315,15 +6089,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringify-object@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" - integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== - dependencies: - get-own-enumerable-property-symbols "^3.0.0" - is-obj "^1.0.1" - is-regexp "^1.0.0" - strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" @@ -6479,7 +6244,7 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" -through@^2.3.6, through@^2.3.8: +through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= @@ -6795,11 +6560,6 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -uuid@^7.0.2: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - v8-compile-cache@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" @@ -6882,13 +6642,6 @@ watchpack@^1.6.1: graceful-fs "^4.1.2" neo-async "^2.5.0" -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= - dependencies: - defaults "^1.0.3" - webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -7077,13 +6830,6 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yaml@^1.7.2: - version "1.9.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.9.2.tgz#f0cfa865f003ab707663e4f04b3956957ea564ed" - integrity sha512-HPT7cGGI0DuRcsO51qC1j9O16Dh1mZ2bnXwsi0jrSpsLz0WxOLSLXfkABVl6bZO629py3CU+OMJtpNHDLB97kg== - dependencies: - "@babel/runtime" "^7.9.2" - yargs-parser@18.x, yargs-parser@^18.1.1: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" From 4f152ba4c9d0aa5302f9fd5f29428601d2ba810e Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Sun, 24 May 2020 19:27:23 -0400 Subject: [PATCH 10/11] prettier format change --- prettier.config.js | 4 +- src/Coverage/CoverageCodeLensProvider.ts | 28 +- src/Coverage/CoverageMapProvider.ts | 24 +- src/Coverage/CoverageOverlay.ts | 48 +- src/Coverage/Formatters/AbstractFormatter.ts | 12 +- src/Coverage/Formatters/DefaultFormatter.ts | 59 +- .../Formatters/GutterFormatter/index.ts | 108 ++- src/Coverage/Formatters/helpers.ts | 6 +- src/Coverage/index.ts | 6 +- src/DebugCodeLens/DebugCodeLens.ts | 23 +- src/DebugCodeLens/DebugCodeLensProvider.ts | 75 +- src/DebugCodeLens/TestState.ts | 4 +- src/DebugCodeLens/index.ts | 4 +- src/DebugConfigurationProvider.ts | 54 +- src/Jest/index.ts | 6 +- src/JestExt.ts | 454 ++++++----- src/JestProcessManagement/JestProcess.ts | 106 +-- .../JestProcessManager.ts | 87 +- src/JestProcessManagement/index.ts | 4 +- src/Settings/index.ts | 38 +- .../SnapshotCodeLensProvider.ts | 37 +- .../SnapshotPreviewProvider.ts | 35 +- src/SnapshotCodeLens/index.ts | 4 +- src/StatusBar.ts | 201 ++--- src/TestParser.ts | 4 +- src/TestResults/TestReconciliationState.ts | 4 +- src/TestResults/TestResult.ts | 82 +- src/TestResults/TestResultProvider.ts | 164 ++-- src/TestResults/index.ts | 6 +- src/appGlobals.ts | 4 +- src/decorations.ts | 12 +- src/diagnostics.ts | 81 +- src/editor.ts | 14 +- src/extension.ts | 77 +- src/extensionManager.ts | 164 ++-- src/helpers.ts | 67 +- src/messaging.ts | 36 +- src/reporter.ts | 6 +- src/types.ts | 4 +- .../Coverage/CoverageCodeLensProvider.test.ts | 74 +- tests/Coverage/CoverageMapProvider.test.ts | 122 +-- tests/Coverage/CoverageOverlay.test.ts | 178 ++--- .../Formatters/DefaultFormatter.test.ts | 208 ++--- .../Formatters/GutterFormatter.test.ts | 179 ++--- tests/Coverage/Formatters/helpers.test.ts | 60 +- tests/DebugCodeLens/DebugCodeLens.test.ts | 36 +- .../DebugCodeLensProvider.test.ts | 312 ++++---- tests/DebugConfigurationProvider.test.ts | 86 +- tests/Jest/index.test.ts | 22 +- tests/JestExt.test.ts | 744 +++++++++--------- .../JestProcessManagement/JestProcess.test.ts | 490 ++++++------ .../JestProcessManager.test.ts | 474 +++++------ tests/Settings/index.test.ts | 18 +- tests/StatusBar.test.ts | 306 +++---- tests/TestResults/TestResult.test.ts | 125 +-- tests/TestResults/TestResultProvider.test.ts | 464 +++++------ tests/decorations.test.ts | 40 +- tests/diagnostics.test.ts | 264 ++++--- tests/editor.test.ts | 62 +- tests/extension.test.ts | 218 ++--- tests/extensionManager.test.ts | 428 +++++----- tests/helpers.test.ts | 155 ++-- tests/messaging.test.ts | 108 +-- 63 files changed, 3815 insertions(+), 3510 deletions(-) diff --git a/prettier.config.js b/prettier.config.js index 575644bb2..5092d77c3 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1,7 +1,7 @@ module.exports = { trailingComma: 'es5', - printWidth: 120, - semi: false, + printWidth: 100, + semi: true, singleQuote: true, overrides: [ { diff --git a/src/Coverage/CoverageCodeLensProvider.ts b/src/Coverage/CoverageCodeLensProvider.ts index 90251d121..d865ee76a 100644 --- a/src/Coverage/CoverageCodeLensProvider.ts +++ b/src/Coverage/CoverageCodeLensProvider.ts @@ -1,33 +1,33 @@ -import * as vscode from 'vscode' +import * as vscode from 'vscode'; -import { GetJestExtByURI } from '../extensionManager' +import { GetJestExtByURI } from '../extensionManager'; export class CoverageCodeLensProvider implements vscode.CodeLensProvider { - private getJestExt: GetJestExtByURI + private getJestExt: GetJestExtByURI; constructor(getJestExt: GetJestExtByURI) { - this.getJestExt = getJestExt + this.getJestExt = getJestExt; } public provideCodeLenses(document: vscode.TextDocument, _token: vscode.CancellationToken) { - const ext = this.getJestExt(document.uri) - const coverage = ext && ext.coverageMapProvider.getFileCoverage(document.fileName) + const ext = this.getJestExt(document.uri); + const coverage = ext && ext.coverageMapProvider.getFileCoverage(document.fileName); if (!coverage) { - return + return; } - const summary = coverage.toSummary() - const json = summary.toJSON() + const summary = coverage.toSummary(); + const json = summary.toJSON(); const metrics = Object.keys(json).reduce((previous, metric) => { - return `${previous}${previous ? ', ' : ''}${metric}: ${json[metric].pct}%` - }, '') + return `${previous}${previous ? ', ' : ''}${metric}: ${json[metric].pct}%`; + }, ''); - const range = new vscode.Range(0, 0, 0, 0) + const range = new vscode.Range(0, 0, 0, 0); const command: vscode.Command = { title: metrics, command: null, - } + }; - return [new vscode.CodeLens(range, command)] + return [new vscode.CodeLens(range, command)]; } } diff --git a/src/Coverage/CoverageMapProvider.ts b/src/Coverage/CoverageMapProvider.ts index 053edf0ee..72e82b289 100644 --- a/src/Coverage/CoverageMapProvider.ts +++ b/src/Coverage/CoverageMapProvider.ts @@ -1,34 +1,34 @@ -import { createSourceMapStore, MapStore } from 'istanbul-lib-source-maps' -import { createCoverageMap, CoverageMap } from 'istanbul-lib-coverage' +import { createSourceMapStore, MapStore } from 'istanbul-lib-source-maps'; +import { createCoverageMap, CoverageMap } from 'istanbul-lib-coverage'; export class CoverageMapProvider { - private mapStore: MapStore + private mapStore: MapStore; /** * Transformed coverage map */ - private _map: CoverageMap + private _map: CoverageMap; constructor() { - this._map = createCoverageMap() - this.mapStore = createSourceMapStore() + this._map = createCoverageMap(); + this.mapStore = createSourceMapStore(); } get map(): CoverageMap { - return this._map + return this._map; } update(obj: CoverageMap | object) { - const map = createCoverageMap(obj) - const transformed = this.mapStore.transformCoverage(map) + const map = createCoverageMap(obj); + const transformed = this.mapStore.transformCoverage(map); if (this._map) { - this._map.merge(transformed.map) + this._map.merge(transformed.map); } else { - this._map = transformed.map + this._map = transformed.map; } } public getFileCoverage(filePath: string) { - return this._map.data[filePath] + return this._map.data[filePath]; } } diff --git a/src/Coverage/CoverageOverlay.ts b/src/Coverage/CoverageOverlay.ts index 2c5acef1e..d9143f799 100644 --- a/src/Coverage/CoverageOverlay.ts +++ b/src/Coverage/CoverageOverlay.ts @@ -1,15 +1,15 @@ -import { AbstractFormatter } from './Formatters/AbstractFormatter' -import { CoverageMapProvider } from './CoverageMapProvider' -import { DefaultFormatter } from './Formatters/DefaultFormatter' -import { GutterFormatter } from './Formatters/GutterFormatter' -import * as vscode from 'vscode' -import { hasDocument } from '../editor' +import { AbstractFormatter } from './Formatters/AbstractFormatter'; +import { CoverageMapProvider } from './CoverageMapProvider'; +import { DefaultFormatter } from './Formatters/DefaultFormatter'; +import { GutterFormatter } from './Formatters/GutterFormatter'; +import * as vscode from 'vscode'; +import { hasDocument } from '../editor'; export class CoverageOverlay { - static readonly defaultVisibility = false - static readonly defaultFormatter = 'DefaultFormatter' - formatter: AbstractFormatter - private _enabled: boolean + static readonly defaultVisibility = false; + static readonly defaultFormatter = 'DefaultFormatter'; + formatter: AbstractFormatter; + private _enabled: boolean; constructor( context: vscode.ExtensionContext, @@ -17,47 +17,47 @@ export class CoverageOverlay { enabled: boolean = CoverageOverlay.defaultVisibility, coverageFormatter: string = CoverageOverlay.defaultFormatter ) { - this._enabled = enabled + this._enabled = enabled; switch (coverageFormatter) { case 'GutterFormatter': - this.formatter = new GutterFormatter(context, coverageMapProvider) - break + this.formatter = new GutterFormatter(context, coverageMapProvider); + break; default: - this.formatter = new DefaultFormatter(coverageMapProvider) - break + this.formatter = new DefaultFormatter(coverageMapProvider); + break; } } get enabled() { - return this._enabled + return this._enabled; } set enabled(value: boolean) { - this._enabled = value - this.updateVisibleEditors() + this._enabled = value; + this.updateVisibleEditors(); } toggleVisibility() { - this._enabled = !this._enabled - this.updateVisibleEditors() + this._enabled = !this._enabled; + this.updateVisibleEditors(); } updateVisibleEditors() { for (const editor of vscode.window.visibleTextEditors) { - this.update(editor) + this.update(editor); } } update(editor: vscode.TextEditor) { if (!hasDocument(editor)) { - return + return; } if (this._enabled) { - this.formatter.format(editor) + this.formatter.format(editor); } else { - this.formatter.clear(editor) + this.formatter.clear(editor); } } } diff --git a/src/Coverage/Formatters/AbstractFormatter.ts b/src/Coverage/Formatters/AbstractFormatter.ts index b013b89da..96ecfbbac 100644 --- a/src/Coverage/Formatters/AbstractFormatter.ts +++ b/src/Coverage/Formatters/AbstractFormatter.ts @@ -1,13 +1,13 @@ -import { CoverageMapProvider } from '../CoverageMapProvider' -import * as vscode from 'vscode' +import { CoverageMapProvider } from '../CoverageMapProvider'; +import * as vscode from 'vscode'; export abstract class AbstractFormatter { - protected coverageMapProvider: CoverageMapProvider + protected coverageMapProvider: CoverageMapProvider; constructor(coverageMapProvider: CoverageMapProvider) { - this.coverageMapProvider = coverageMapProvider + this.coverageMapProvider = coverageMapProvider; } - abstract format(editor: vscode.TextEditor) - abstract clear(editor: vscode.TextEditor) + abstract format(editor: vscode.TextEditor); + abstract clear(editor: vscode.TextEditor); } diff --git a/src/Coverage/Formatters/DefaultFormatter.ts b/src/Coverage/Formatters/DefaultFormatter.ts index e1d62bcd9..9b4119934 100644 --- a/src/Coverage/Formatters/DefaultFormatter.ts +++ b/src/Coverage/Formatters/DefaultFormatter.ts @@ -1,71 +1,78 @@ -import { AbstractFormatter } from './AbstractFormatter' -import * as vscode from 'vscode' -import { FileCoverage } from 'istanbul-lib-coverage' -import { isValidLocation } from './helpers' +import { AbstractFormatter } from './AbstractFormatter'; +import * as vscode from 'vscode'; +import { FileCoverage } from 'istanbul-lib-coverage'; +import { isValidLocation } from './helpers'; const uncoveredBranch = vscode.window.createTextEditorDecorationType({ backgroundColor: 'rgba(216,134,123,0.4)', overviewRulerColor: 'rgba(216,134,123,0.8)', overviewRulerLane: vscode.OverviewRulerLane.Left, -}) +}); const uncoveredLine = vscode.window.createTextEditorDecorationType({ isWholeLine: true, backgroundColor: 'rgba(216,134,123,0.4)', overviewRulerColor: 'rgba(216,134,123,0.8)', overviewRulerLane: vscode.OverviewRulerLane.Left, -}) +}); export class DefaultFormatter extends AbstractFormatter { format(editor: vscode.TextEditor) { - const fileCoverage = this.coverageMapProvider.getFileCoverage(editor.document.fileName) + const fileCoverage = this.coverageMapProvider.getFileCoverage(editor.document.fileName); if (!fileCoverage) { - return + return; } - this.formatBranches(editor, fileCoverage) - this.formatUncoveredLines(editor, fileCoverage) + this.formatBranches(editor, fileCoverage); + this.formatUncoveredLines(editor, fileCoverage); } formatBranches(editor: vscode.TextEditor, fileCoverage: FileCoverage) { - const ranges = [] + const ranges = []; Object.keys(fileCoverage.b).forEach((branchIndex) => { fileCoverage.b[branchIndex].forEach((hitCount, locationIndex) => { if (hitCount > 0) { - return + return; } - const branch = fileCoverage.branchMap[branchIndex].locations[locationIndex] + const branch = fileCoverage.branchMap[branchIndex].locations[locationIndex]; if (!isValidLocation(branch)) { - return + return; } // If the value is `null`, then set it to the first character on its // line. - const endColumn = branch.end.column || 0 + const endColumn = branch.end.column || 0; - ranges.push(new vscode.Range(branch.start.line - 1, branch.start.column, branch.end.line - 1, endColumn)) - }) - }) + ranges.push( + new vscode.Range( + branch.start.line - 1, + branch.start.column, + branch.end.line - 1, + endColumn + ) + ); + }); + }); - editor.setDecorations(uncoveredBranch, ranges) + editor.setDecorations(uncoveredBranch, ranges); } formatUncoveredLines(editor: vscode.TextEditor, fileCoverage: FileCoverage) { - const lines = fileCoverage.getUncoveredLines() + const lines = fileCoverage.getUncoveredLines(); - const ranges = [] + const ranges = []; for (const oneBasedLineNumber of lines) { - const zeroBasedLineNumber = Number(oneBasedLineNumber) - 1 - ranges.push(new vscode.Range(zeroBasedLineNumber, 0, zeroBasedLineNumber, 0)) + const zeroBasedLineNumber = Number(oneBasedLineNumber) - 1; + ranges.push(new vscode.Range(zeroBasedLineNumber, 0, zeroBasedLineNumber, 0)); } - editor.setDecorations(uncoveredLine, ranges) + editor.setDecorations(uncoveredLine, ranges); } clear(editor: vscode.TextEditor) { - editor.setDecorations(uncoveredLine, []) - editor.setDecorations(uncoveredBranch, []) + editor.setDecorations(uncoveredLine, []); + editor.setDecorations(uncoveredBranch, []); } } diff --git a/src/Coverage/Formatters/GutterFormatter/index.ts b/src/Coverage/Formatters/GutterFormatter/index.ts index 0a9a3ca85..b6fc2b055 100644 --- a/src/Coverage/Formatters/GutterFormatter/index.ts +++ b/src/Coverage/Formatters/GutterFormatter/index.ts @@ -1,30 +1,32 @@ -import { CoverageMapProvider } from '../../CoverageMapProvider' -import { AbstractFormatter } from '../AbstractFormatter' -import * as vscode from 'vscode' -import { FileCoverage } from 'istanbul-lib-coverage' -import { isValidLocation } from '../helpers' +import { CoverageMapProvider } from '../../CoverageMapProvider'; +import { AbstractFormatter } from '../AbstractFormatter'; +import * as vscode from 'vscode'; +import { FileCoverage } from 'istanbul-lib-coverage'; +import { isValidLocation } from '../helpers'; export interface CoverageLines { - covered: vscode.Range[] - partiallyCovered: vscode.Range[] - uncovered: vscode.Range[] + covered: vscode.Range[]; + partiallyCovered: vscode.Range[]; + uncovered: vscode.Range[]; } export class GutterFormatter extends AbstractFormatter { - private uncoveredLine: vscode.TextEditorDecorationType - private partiallyCoveredLine: vscode.TextEditorDecorationType - private coveredLine: vscode.TextEditorDecorationType + private uncoveredLine: vscode.TextEditorDecorationType; + private partiallyCoveredLine: vscode.TextEditorDecorationType; + private coveredLine: vscode.TextEditorDecorationType; constructor(context: vscode.ExtensionContext, coverageMapProvider: CoverageMapProvider) { - super(coverageMapProvider) + super(coverageMapProvider); this.uncoveredLine = vscode.window.createTextEditorDecorationType({ isWholeLine: true, backgroundColor: '', overviewRulerColor: 'rgba(121, 31, 10, 0.75)', overviewRulerLane: vscode.OverviewRulerLane.Left, - gutterIconPath: context.asAbsolutePath('./src/Coverage/Formatters/GutterFormatter/uncovered-gutter-icon.svg'), - }) + gutterIconPath: context.asAbsolutePath( + './src/Coverage/Formatters/GutterFormatter/uncovered-gutter-icon.svg' + ), + }); this.partiallyCoveredLine = vscode.window.createTextEditorDecorationType({ backgroundColor: 'rgba(121, 86, 10, 0.75)', @@ -33,28 +35,30 @@ export class GutterFormatter extends AbstractFormatter { gutterIconPath: context.asAbsolutePath( './src/Coverage/Formatters/GutterFormatter/partially-covered-gutter-icon.svg' ), - }) + }); this.coveredLine = vscode.window.createTextEditorDecorationType({ isWholeLine: true, backgroundColor: '', overviewRulerColor: '', overviewRulerLane: vscode.OverviewRulerLane.Left, - gutterIconPath: context.asAbsolutePath('./src/Coverage/Formatters/GutterFormatter/covered-gutter-icon.svg'), - }) + gutterIconPath: context.asAbsolutePath( + './src/Coverage/Formatters/GutterFormatter/covered-gutter-icon.svg' + ), + }); } format(editor: vscode.TextEditor) { - const fileCoverage = this.coverageMapProvider.getFileCoverage(editor.document.fileName) + const fileCoverage = this.coverageMapProvider.getFileCoverage(editor.document.fileName); if (!fileCoverage) { - return + return; } - const coverageFormatting = this.computeFormatting(editor, fileCoverage) + const coverageFormatting = this.computeFormatting(editor, fileCoverage); - editor.setDecorations(this.coveredLine, coverageFormatting.covered) - editor.setDecorations(this.uncoveredLine, coverageFormatting.uncovered) - editor.setDecorations(this.partiallyCoveredLine, coverageFormatting.partiallyCovered) + editor.setDecorations(this.coveredLine, coverageFormatting.covered); + editor.setDecorations(this.uncoveredLine, coverageFormatting.uncovered); + editor.setDecorations(this.partiallyCoveredLine, coverageFormatting.partiallyCovered); } computeFormatting(editor: vscode.TextEditor, fileCoverage: FileCoverage): CoverageLines { @@ -62,46 +66,64 @@ export class GutterFormatter extends AbstractFormatter { covered: [], partiallyCovered: [], uncovered: [], - } + }; - const uncoveredLines = fileCoverage.getUncoveredLines() + const uncoveredLines = fileCoverage.getUncoveredLines(); for (let line = 1; line <= editor.document.lineCount; line++) { - const zeroBasedLineNumber = line - 1 + const zeroBasedLineNumber = line - 1; if (uncoveredLines.indexOf(line.toString()) >= 0) { - coverageFormatting.uncovered.push(new vscode.Range(zeroBasedLineNumber, 0, zeroBasedLineNumber, 0)) + coverageFormatting.uncovered.push( + new vscode.Range(zeroBasedLineNumber, 0, zeroBasedLineNumber, 0) + ); } else { - coverageFormatting.covered.push(new vscode.Range(zeroBasedLineNumber, 0, zeroBasedLineNumber, 0)) + coverageFormatting.covered.push( + new vscode.Range(zeroBasedLineNumber, 0, zeroBasedLineNumber, 0) + ); } } Object.keys(fileCoverage.b).forEach((branchIndex) => { fileCoverage.b[branchIndex].forEach((hitCount, locationIndex) => { if (hitCount > 0) { - return + return; } - const branch = fileCoverage.branchMap[branchIndex].locations[locationIndex] + const branch = fileCoverage.branchMap[branchIndex].locations[locationIndex]; if (!isValidLocation(branch)) { - return + return; } - const partialLineRange = new vscode.Range(branch.start.line - 1, 0, branch.start.line - 1, 0) - coverageFormatting.covered = coverageFormatting.covered.filter((range) => !range.isEqual(partialLineRange)) - coverageFormatting.uncovered = coverageFormatting.uncovered.filter((range) => !range.isEqual(partialLineRange)) + const partialLineRange = new vscode.Range( + branch.start.line - 1, + 0, + branch.start.line - 1, + 0 + ); + coverageFormatting.covered = coverageFormatting.covered.filter( + (range) => !range.isEqual(partialLineRange) + ); + coverageFormatting.uncovered = coverageFormatting.uncovered.filter( + (range) => !range.isEqual(partialLineRange) + ); coverageFormatting.partiallyCovered.push( - new vscode.Range(branch.start.line - 1, branch.start.column, branch.end.line - 1, branch.end.column) - ) - }) - }) - - return coverageFormatting + new vscode.Range( + branch.start.line - 1, + branch.start.column, + branch.end.line - 1, + branch.end.column + ) + ); + }); + }); + + return coverageFormatting; } clear(editor: vscode.TextEditor) { - editor.setDecorations(this.coveredLine, []) - editor.setDecorations(this.partiallyCoveredLine, []) - editor.setDecorations(this.uncoveredLine, []) + editor.setDecorations(this.coveredLine, []); + editor.setDecorations(this.partiallyCoveredLine, []); + editor.setDecorations(this.uncoveredLine, []); } } diff --git a/src/Coverage/Formatters/helpers.ts b/src/Coverage/Formatters/helpers.ts index 55ae0d79b..e062ec56e 100644 --- a/src/Coverage/Formatters/helpers.ts +++ b/src/Coverage/Formatters/helpers.ts @@ -1,9 +1,9 @@ -import { Location, Position } from 'istanbul-lib-coverage' +import { Location, Position } from 'istanbul-lib-coverage'; export function isValidPosition(p: Position) { - return (p || false) && p.line !== null && p.line >= 0 + return (p || false) && p.line !== null && p.line >= 0; } export function isValidLocation(l: Location) { - return isValidPosition(l.start) && isValidPosition(l.end) + return isValidPosition(l.start) && isValidPosition(l.end); } diff --git a/src/Coverage/index.ts b/src/Coverage/index.ts index af36d6511..40e762b20 100644 --- a/src/Coverage/index.ts +++ b/src/Coverage/index.ts @@ -1,3 +1,3 @@ -export * from './CoverageMapProvider' -export * from './CoverageOverlay' -export * from './CoverageCodeLensProvider' +export * from './CoverageMapProvider'; +export * from './CoverageOverlay'; +export * from './CoverageCodeLensProvider'; diff --git a/src/DebugCodeLens/DebugCodeLens.ts b/src/DebugCodeLens/DebugCodeLens.ts index 01faf7181..893cfc5f5 100644 --- a/src/DebugCodeLens/DebugCodeLens.ts +++ b/src/DebugCodeLens/DebugCodeLens.ts @@ -1,14 +1,19 @@ -import * as vscode from 'vscode' +import * as vscode from 'vscode'; export class DebugCodeLens extends vscode.CodeLens { - readonly fileName: string - readonly testName: string - readonly document: vscode.TextDocument + readonly fileName: string; + readonly testName: string; + readonly document: vscode.TextDocument; - constructor(document: vscode.TextDocument, range: vscode.Range, fileName: string, testName: string) { - super(range) - this.document = document - this.fileName = fileName - this.testName = testName + constructor( + document: vscode.TextDocument, + range: vscode.Range, + fileName: string, + testName: string + ) { + super(range); + this.document = document; + this.fileName = fileName; + this.testName = testName; } } diff --git a/src/DebugCodeLens/DebugCodeLensProvider.ts b/src/DebugCodeLens/DebugCodeLensProvider.ts index 6b9466a3a..bd1b61c72 100644 --- a/src/DebugCodeLens/DebugCodeLensProvider.ts +++ b/src/DebugCodeLens/DebugCodeLensProvider.ts @@ -1,79 +1,82 @@ -import * as vscode from 'vscode' -import { extensionName } from '../appGlobals' -import { escapeRegExp } from '../helpers' -import { basename } from 'path' -import { DebugCodeLens } from './DebugCodeLens' -import { TestReconciliationState } from '../TestResults' -import { TestState, TestStateByTestReconciliationState } from './TestState' -import { GetJestExtByURI } from '../extensionManager' +import * as vscode from 'vscode'; +import { extensionName } from '../appGlobals'; +import { escapeRegExp } from '../helpers'; +import { basename } from 'path'; +import { DebugCodeLens } from './DebugCodeLens'; +import { TestReconciliationState } from '../TestResults'; +import { TestState, TestStateByTestReconciliationState } from './TestState'; +import { GetJestExtByURI } from '../extensionManager'; export class DebugCodeLensProvider implements vscode.CodeLensProvider { - onDidChange: vscode.EventEmitter<void> - private _showWhenTestStateIn: TestState[] - private getJestExt: GetJestExtByURI + onDidChange: vscode.EventEmitter<void>; + private _showWhenTestStateIn: TestState[]; + private getJestExt: GetJestExtByURI; constructor(getJestExt: GetJestExtByURI, showWhenTestStateIn: TestState[] = []) { - this.getJestExt = getJestExt - this._showWhenTestStateIn = showWhenTestStateIn - this.onDidChange = new vscode.EventEmitter() + this.getJestExt = getJestExt; + this._showWhenTestStateIn = showWhenTestStateIn; + this.onDidChange = new vscode.EventEmitter(); } get showWhenTestStateIn() { - return this._showWhenTestStateIn + return this._showWhenTestStateIn; } set showWhenTestStateIn(value: TestState[]) { - this._showWhenTestStateIn = value - this.onDidChange.fire() + this._showWhenTestStateIn = value; + this.onDidChange.fire(); } get onDidChangeCodeLenses(): vscode.Event<void> { - return this.onDidChange.event + return this.onDidChange.event; } provideCodeLenses(document: vscode.TextDocument, _: vscode.CancellationToken): vscode.CodeLens[] { - const result = [] - const ext = this.getJestExt(document.uri) + const result = []; + const ext = this.getJestExt(document.uri); if (!ext || this._showWhenTestStateIn.length === 0 || document.isUntitled) { - return result + return result; } - const filePath = document.fileName - const testResults = ext.testResultProvider.getResults(filePath) - const fileName = basename(document.fileName) + const filePath = document.fileName; + const testResults = ext.testResultProvider.getResults(filePath); + const fileName = basename(document.fileName); for (const test of testResults) { if (!this.showCodeLensAboveTest(test)) { - continue + continue; } - const start = new vscode.Position(test.start.line, test.start.column) - const end = new vscode.Position(test.end.line, test.start.column + 5) - const range = new vscode.Range(start, end) - result.push(new DebugCodeLens(document, range, fileName, test.name)) + const start = new vscode.Position(test.start.line, test.start.column); + const end = new vscode.Position(test.end.line, test.start.column + 5); + const range = new vscode.Range(start, end); + result.push(new DebugCodeLens(document, range, fileName, test.name)); } - return result + return result; } showCodeLensAboveTest(test: { status: TestReconciliationState }) { - const state = TestStateByTestReconciliationState[test.status] - return this._showWhenTestStateIn.includes(state) + const state = TestStateByTestReconciliationState[test.status]; + return this._showWhenTestStateIn.includes(state); } - resolveCodeLens(codeLens: vscode.CodeLens, _: vscode.CancellationToken): vscode.ProviderResult<vscode.CodeLens> { + resolveCodeLens( + codeLens: vscode.CodeLens, + _: vscode.CancellationToken + ): vscode.ProviderResult<vscode.CodeLens> { if (codeLens instanceof DebugCodeLens) { codeLens.command = { arguments: [codeLens.document, codeLens.fileName, escapeRegExp(codeLens.testName)], command: `${extensionName}.run-test`, title: 'Debug', - } + }; } - return codeLens + return codeLens; } didChange() { - this.onDidChange.fire() + this.onDidChange.fire(); } } diff --git a/src/DebugCodeLens/TestState.ts b/src/DebugCodeLens/TestState.ts index 5ffff547b..48fd748eb 100644 --- a/src/DebugCodeLens/TestState.ts +++ b/src/DebugCodeLens/TestState.ts @@ -1,4 +1,4 @@ -import { TestReconciliationState } from '../TestResults' +import { TestReconciliationState } from '../TestResults'; export enum TestState { Fail = 'fail', @@ -13,4 +13,4 @@ export const TestStateByTestReconciliationState = { [TestReconciliationState.KnownSkip]: TestState.Skip, [TestReconciliationState.KnownSuccess]: TestState.Pass, [TestReconciliationState.Unknown]: TestState.Unknown, -} +}; diff --git a/src/DebugCodeLens/index.ts b/src/DebugCodeLens/index.ts index b9e09cce4..ada340ea8 100644 --- a/src/DebugCodeLens/index.ts +++ b/src/DebugCodeLens/index.ts @@ -1,2 +1,2 @@ -export { DebugCodeLensProvider } from './DebugCodeLensProvider' -export { TestState } from './TestState' +export { DebugCodeLensProvider } from './DebugCodeLensProvider'; +export { TestState } from './TestState'; diff --git a/src/DebugConfigurationProvider.ts b/src/DebugConfigurationProvider.ts index a9ba6c9e4..e0f6a0994 100644 --- a/src/DebugConfigurationProvider.ts +++ b/src/DebugConfigurationProvider.ts @@ -1,17 +1,17 @@ -import * as vscode from 'vscode' -import { getTestCommand, isCreateReactAppTestCommand } from './helpers' +import * as vscode from 'vscode'; +import { getTestCommand, isCreateReactAppTestCommand } from './helpers'; export class DebugConfigurationProvider implements vscode.DebugConfigurationProvider { - private fileNameToRun = '' - private testToRun = '' + private fileNameToRun = ''; + private testToRun = ''; /** * Prepares injecting the name of the test, which has to be debugged, into the `DebugConfiguration`, * This function has to be called before `vscode.debug.startDebugging`. */ public prepareTestRun(fileNameToRun: string, testToRun: string) { - this.fileNameToRun = fileNameToRun - this.testToRun = testToRun + this.fileNameToRun = fileNameToRun; + this.testToRun = testToRun; } resolveDebugConfiguration( @@ -20,32 +20,35 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv _token?: vscode.CancellationToken ) { if (debugConfiguration.name !== 'vscode-jest-tests') { - return debugConfiguration + return debugConfiguration; } if (!debugConfiguration.env) { - debugConfiguration.env = {} + debugConfiguration.env = {}; } // necessary for running CRA test scripts in non-watch mode - debugConfiguration.env.CI = 'vscode-jest-tests' + debugConfiguration.env.CI = 'vscode-jest-tests'; if (!debugConfiguration.args) { - debugConfiguration.args = [] + debugConfiguration.args = []; } if (this.fileNameToRun) { - debugConfiguration.args.push(this.fileNameToRun) + debugConfiguration.args.push(this.fileNameToRun); if (this.testToRun) { - debugConfiguration.args.push('--testNamePattern') - debugConfiguration.args.push(this.testToRun) + debugConfiguration.args.push('--testNamePattern'); + debugConfiguration.args.push(this.testToRun); } - this.fileNameToRun = '' - this.testToRun = '' + this.fileNameToRun = ''; + this.testToRun = ''; } - return debugConfiguration + return debugConfiguration; } - provideDebugConfigurations(folder: vscode.WorkspaceFolder | undefined, _token?: vscode.CancellationToken) { + provideDebugConfigurations( + folder: vscode.WorkspaceFolder | undefined, + _token?: vscode.CancellationToken + ) { // default jest config according to: // https://github.com/Microsoft/vscode-recipes/tree/master/debugging-jest-tests#configure-launchjson-file-for-your-test-framework @@ -62,20 +65,21 @@ export class DebugConfigurationProvider implements vscode.DebugConfigurationProv console: 'integratedTerminal', internalConsoleOptions: 'neverOpen', disableOptimisticBPs: true, - } + }; - const testCommand = folder && getTestCommand(folder.uri.fsPath) + const testCommand = folder && getTestCommand(folder.uri.fsPath); if (isCreateReactAppTestCommand(testCommand)) { - const craCommand = testCommand.split(' ') + const craCommand = testCommand.split(' '); // Settings specific for projects bootstrapped with `create-react-app` - debugConfiguration.runtimeExecutable = '${workspaceFolder}/node_modules/.bin/' + craCommand.shift() - debugConfiguration.args = [...craCommand, ...debugConfiguration.args] - debugConfiguration.protocol = 'inspector' + debugConfiguration.runtimeExecutable = + '${workspaceFolder}/node_modules/.bin/' + craCommand.shift(); + debugConfiguration.args = [...craCommand, ...debugConfiguration.args]; + debugConfiguration.protocol = 'inspector'; } else { // Plain jest setup - debugConfiguration.program = '${workspaceFolder}/node_modules/jest/bin/jest' + debugConfiguration.program = '${workspaceFolder}/node_modules/jest/bin/jest'; } - return [debugConfiguration] + return [debugConfiguration]; } } diff --git a/src/Jest/index.ts b/src/Jest/index.ts index 72166fe8a..ce8f4a977 100644 --- a/src/Jest/index.ts +++ b/src/Jest/index.ts @@ -4,8 +4,8 @@ export enum WatchMode { WatchAll = 'watchAll', } -const IS_OUTSIDE_REPOSITORY_REGEXP = /Test suite failed to run[\s\S]*fatal:[\s\S]*is outside repository/im -const WATCH_IS_NOT_SUPPORTED_REGEXP = /^s*--watch is not supported without git\/hg, please use --watchAlls*/im +const IS_OUTSIDE_REPOSITORY_REGEXP = /Test suite failed to run[\s\S]*fatal:[\s\S]*is outside repository/im; +const WATCH_IS_NOT_SUPPORTED_REGEXP = /^s*--watch is not supported without git\/hg, please use --watchAlls*/im; export const isWatchNotSupported = (str = '') => - IS_OUTSIDE_REPOSITORY_REGEXP.test(str) || WATCH_IS_NOT_SUPPORTED_REGEXP.test(str) + IS_OUTSIDE_REPOSITORY_REGEXP.test(str) || WATCH_IS_NOT_SUPPORTED_REGEXP.test(str); diff --git a/src/JestExt.ts b/src/JestExt.ts index 278a4b4c8..5605d0c8d 100644 --- a/src/JestExt.ts +++ b/src/JestExt.ts @@ -1,67 +1,72 @@ -import * as vscode from 'vscode' -import { ProjectWorkspace, JestTotalResults } from 'jest-editor-support' +import * as vscode from 'vscode'; +import { ProjectWorkspace, JestTotalResults } from 'jest-editor-support'; -import * as decorations from './decorations' -import { PluginResourceSettings } from './Settings' -import { statusBar, Status, StatusBar, Mode } from './StatusBar' +import * as decorations from './decorations'; +import { PluginResourceSettings } from './Settings'; +import { statusBar, Status, StatusBar, Mode } from './StatusBar'; import { TestReconciliationState, TestResultProvider, TestResult, resultsWithLowerCaseWindowsDriveLetters, SortedTestResults, -} from './TestResults' -import { pathToJest, pathToConfig, cleanAnsi } from './helpers' -import { CoverageMapProvider } from './Coverage' -import { updateDiagnostics, updateCurrentDiagnostics, resetDiagnostics, failedSuiteCount } from './diagnostics' -import { DebugCodeLensProvider } from './DebugCodeLens' -import { DebugConfigurationProvider } from './DebugConfigurationProvider' -import { DecorationOptions } from './types' -import { isOpenInMultipleEditors } from './editor' -import { CoverageOverlay } from './Coverage/CoverageOverlay' -import { JestProcess, JestProcessManager } from './JestProcessManagement' -import { isWatchNotSupported, WatchMode } from './Jest' -import * as messaging from './messaging' -import { resultsWithoutAnsiEscapeSequence } from './TestResults/TestResult' +} from './TestResults'; +import { pathToJest, pathToConfig, cleanAnsi } from './helpers'; +import { CoverageMapProvider } from './Coverage'; +import { + updateDiagnostics, + updateCurrentDiagnostics, + resetDiagnostics, + failedSuiteCount, +} from './diagnostics'; +import { DebugCodeLensProvider } from './DebugCodeLens'; +import { DebugConfigurationProvider } from './DebugConfigurationProvider'; +import { DecorationOptions } from './types'; +import { isOpenInMultipleEditors } from './editor'; +import { CoverageOverlay } from './Coverage/CoverageOverlay'; +import { JestProcess, JestProcessManager } from './JestProcessManagement'; +import { isWatchNotSupported, WatchMode } from './Jest'; +import * as messaging from './messaging'; +import { resultsWithoutAnsiEscapeSequence } from './TestResults/TestResult'; interface InstanceSettings { - multirootEnv: boolean + multirootEnv: boolean; } export class JestExt { - coverageMapProvider: CoverageMapProvider - coverageOverlay: CoverageOverlay + coverageMapProvider: CoverageMapProvider; + coverageOverlay: CoverageOverlay; - testResultProvider: TestResultProvider - debugCodeLensProvider: DebugCodeLensProvider - debugConfigurationProvider: DebugConfigurationProvider + testResultProvider: TestResultProvider; + debugCodeLensProvider: DebugCodeLensProvider; + debugConfigurationProvider: DebugConfigurationProvider; // So you can read what's going on - channel: vscode.OutputChannel + channel: vscode.OutputChannel; - failingAssertionDecorators: { [fileName: string]: vscode.TextEditorDecorationType[] } + failingAssertionDecorators: { [fileName: string]: vscode.TextEditorDecorationType[] }; - private jestWorkspace: ProjectWorkspace - private pluginSettings: PluginResourceSettings - private workspaceFolder: vscode.WorkspaceFolder - private instanceSettings: InstanceSettings + private jestWorkspace: ProjectWorkspace; + private pluginSettings: PluginResourceSettings; + private workspaceFolder: vscode.WorkspaceFolder; + private instanceSettings: InstanceSettings; // The ability to show fails in the problems section - private failDiagnostics: vscode.DiagnosticCollection + private failDiagnostics: vscode.DiagnosticCollection; - private passingItStyle: vscode.TextEditorDecorationType - private failingItStyle: vscode.TextEditorDecorationType - private skipItStyle: vscode.TextEditorDecorationType - private unknownItStyle: vscode.TextEditorDecorationType + private passingItStyle: vscode.TextEditorDecorationType; + private failingItStyle: vscode.TextEditorDecorationType; + private skipItStyle: vscode.TextEditorDecorationType; + private unknownItStyle: vscode.TextEditorDecorationType; - private parsingTestFile = false + private parsingTestFile = false; // We have to keep track of our inline assert fails to remove later - private jestProcessManager: JestProcessManager - private jestProcess: JestProcess + private jestProcessManager: JestProcessManager; + private jestProcess: JestProcess; - private status: ReturnType<StatusBar['bind']> + private status: ReturnType<StatusBar['bind']>; constructor( context: vscode.ExtensionContext, @@ -74,55 +79,55 @@ export class JestExt { failDiagnostics: vscode.DiagnosticCollection, instanceSettings: InstanceSettings ) { - this.workspaceFolder = workspaceFolder - this.jestWorkspace = jestWorkspace - this.channel = outputChannel - this.failingAssertionDecorators = {} - this.failDiagnostics = failDiagnostics - this.pluginSettings = pluginSettings - this.debugCodeLensProvider = debugCodeLensProvider - this.instanceSettings = instanceSettings - - this.coverageMapProvider = new CoverageMapProvider() + this.workspaceFolder = workspaceFolder; + this.jestWorkspace = jestWorkspace; + this.channel = outputChannel; + this.failingAssertionDecorators = {}; + this.failDiagnostics = failDiagnostics; + this.pluginSettings = pluginSettings; + this.debugCodeLensProvider = debugCodeLensProvider; + this.instanceSettings = instanceSettings; + + this.coverageMapProvider = new CoverageMapProvider(); this.coverageOverlay = new CoverageOverlay( context, this.coverageMapProvider, pluginSettings.showCoverageOnLoad, pluginSettings.coverageFormatter - ) - this.jestWorkspace.collectCoverage = pluginSettings.showCoverageOnLoad + ); + this.jestWorkspace.collectCoverage = pluginSettings.showCoverageOnLoad; - this.testResultProvider = new TestResultProvider(this.pluginSettings.debugMode) - this.debugConfigurationProvider = debugConfigurationProvider + this.testResultProvider = new TestResultProvider(this.pluginSettings.debugMode); + this.debugConfigurationProvider = debugConfigurationProvider; this.jestProcessManager = new JestProcessManager({ projectWorkspace: jestWorkspace, runAllTestsFirstInWatchMode: this.pluginSettings.runAllTestsFirst, - }) + }); - this.status = statusBar.bind(workspaceFolder.name) - this.handleJestEditorSupportEvent = this.handleJestEditorSupportEvent.bind(this) + this.status = statusBar.bind(workspaceFolder.name); + this.handleJestEditorSupportEvent = this.handleJestEditorSupportEvent.bind(this); // The theme stuff - this.setupDecorators() + this.setupDecorators(); // The bottom bar thing - this.setupStatusBar() + this.setupStatusBar(); // reset the jest diagnostics - resetDiagnostics(this.failDiagnostics) + resetDiagnostics(this.failDiagnostics); // If we should start the process by default, do so if (this.pluginSettings.autoEnable) { - this.startProcess() + this.startProcess(); } else { - this.channel.appendLine('Skipping initial Jest runner process start.') + this.channel.appendLine('Skipping initial Jest runner process start.'); } } public startProcess() { if (this.jestProcessManager.numberOfProcesses > 0) { // tslint:disable-next-line no-console - console.warn(`process is already running, will not start a new process.`) - return + console.warn(`process is already running, will not start a new process.`); + return; } this.jestProcess = this.jestProcessManager.startJestProcess({ @@ -130,180 +135,206 @@ export class JestExt { keepAlive: true, exitCallback: (jestProcess, jestProcessInWatchMode) => { if (jestProcessInWatchMode) { - this.jestProcess = jestProcessInWatchMode + this.jestProcess = jestProcessInWatchMode; - this.channel.appendLine('Finished running all tests. Starting watch mode.') - this.updateStatusBar('running', 'Starting watch mode', false) + this.channel.appendLine('Finished running all tests. Starting watch mode.'); + this.updateStatusBar('running', 'Starting watch mode', false); - this.assignHandlers(this.jestProcess) + this.assignHandlers(this.jestProcess); } else { - this.updateStatusBar('stopped', undefined, false) + this.updateStatusBar('stopped', undefined, false); if (!jestProcess.stopRequested()) { - let msg = 'Starting Jest in Watch mode failed too many times and has been stopped.' + let msg = 'Starting Jest in Watch mode failed too many times and has been stopped.'; if (this.instanceSettings.multirootEnv) { - const folder = this.workspaceFolder.name - msg = `(${folder}) ${msg}\nIf this is expected, consider adding '${folder}' to disabledWorkspaceFolders` + const folder = this.workspaceFolder.name; + msg = `(${folder}) ${msg}\nIf this is expected, consider adding '${folder}' to disabledWorkspaceFolders`; } - this.channel.appendLine(`${msg}\n see troubleshooting: ${messaging.TROUBLESHOOTING_URL}`) - this.channel.show(true) - messaging.systemErrorMessage(msg, messaging.showTroubleshootingAction) + this.channel.appendLine( + `${msg}\n see troubleshooting: ${messaging.TROUBLESHOOTING_URL}` + ); + this.channel.show(true); + messaging.systemErrorMessage(msg, messaging.showTroubleshootingAction); } } }, - }) + }); - this.assignHandlers(this.jestProcess) + this.assignHandlers(this.jestProcess); } public stopProcess() { - this.channel.appendLine('Closing Jest') + this.channel.appendLine('Closing Jest'); return this.jestProcessManager.stopAll().then(() => { - this.updateStatusBar('stopped') - }) + this.updateStatusBar('stopped'); + }); } public restartProcess() { return this.stopProcess().then(() => { - this.startProcess() - }) + this.startProcess(); + }); } public triggerUpdateActiveEditor(editor: vscode.TextEditor) { - this.coverageOverlay.updateVisibleEditors() + this.coverageOverlay.updateVisibleEditors(); if (!this.canUpdateActiveEditor(editor)) { - return + return; } // not sure why we need to protect this block with parsingTestFile ? // using an ivar as a locking mechanism has bad smell // TODO: refactor maybe? - this.parsingTestFile = true + this.parsingTestFile = true; - const filePath = editor.document.fileName - const testResults = this.testResultProvider.getSortedResults(filePath) + const filePath = editor.document.fileName; + const testResults = this.testResultProvider.getSortedResults(filePath); - this.updateDecorators(testResults, editor) - updateCurrentDiagnostics(testResults.fail, this.failDiagnostics, editor) + this.updateDecorators(testResults, editor); + updateCurrentDiagnostics(testResults.fail, this.failDiagnostics, editor); - this.parsingTestFile = false + this.parsingTestFile = false; } public triggerUpdateSettings(updatedSettings: PluginResourceSettings) { - this.pluginSettings = updatedSettings + this.pluginSettings = updatedSettings; - this.jestWorkspace.rootPath = updatedSettings.rootPath - this.jestWorkspace.pathToJest = pathToJest(updatedSettings) - this.jestWorkspace.pathToConfig = pathToConfig(updatedSettings) + this.jestWorkspace.rootPath = updatedSettings.rootPath; + this.jestWorkspace.pathToJest = pathToJest(updatedSettings); + this.jestWorkspace.pathToConfig = pathToConfig(updatedSettings); // debug - this.jestWorkspace.debug = updatedSettings.debugMode - this.testResultProvider.verbose = updatedSettings.debugMode + this.jestWorkspace.debug = updatedSettings.debugMode; + this.testResultProvider.verbose = updatedSettings.debugMode; // coverage const showCoverage = - this.coverageOverlay.enabled === undefined ? updatedSettings.showCoverageOnLoad : this.coverageOverlay.enabled - this.jestWorkspace.collectCoverage = showCoverage - this.coverageOverlay.enabled = showCoverage + this.coverageOverlay.enabled === undefined + ? updatedSettings.showCoverageOnLoad + : this.coverageOverlay.enabled; + this.jestWorkspace.collectCoverage = showCoverage; + this.coverageOverlay.enabled = showCoverage; - this.restartProcess() + this.restartProcess(); } updateDecorators(testResults: SortedTestResults, editor: vscode.TextEditor) { // Dots const styleMap = [ - { data: testResults.success, decorationType: this.passingItStyle, state: TestReconciliationState.KnownSuccess }, - { data: testResults.fail, decorationType: this.failingItStyle, state: TestReconciliationState.KnownFail }, - { data: testResults.skip, decorationType: this.skipItStyle, state: TestReconciliationState.KnownSkip }, - { data: testResults.unknown, decorationType: this.unknownItStyle, state: TestReconciliationState.Unknown }, - ] + { + data: testResults.success, + decorationType: this.passingItStyle, + state: TestReconciliationState.KnownSuccess, + }, + { + data: testResults.fail, + decorationType: this.failingItStyle, + state: TestReconciliationState.KnownFail, + }, + { + data: testResults.skip, + decorationType: this.skipItStyle, + state: TestReconciliationState.KnownSkip, + }, + { + data: testResults.unknown, + decorationType: this.unknownItStyle, + state: TestReconciliationState.Unknown, + }, + ]; styleMap.forEach((style) => { - const decorators = this.generateDotsForItBlocks(style.data, style.state) - editor.setDecorations(style.decorationType, decorators) - }) + const decorators = this.generateDotsForItBlocks(style.data, style.state); + editor.setDecorations(style.decorationType, decorators); + }); // Debug CodeLens - this.debugCodeLensProvider.didChange() + this.debugCodeLensProvider.didChange(); // Inline error messages - this.resetInlineErrorDecorators(editor) + this.resetInlineErrorDecorators(editor); if (this.pluginSettings.enableInlineErrorMessages) { - const fileName = editor.document.fileName + const fileName = editor.document.fileName; testResults.fail.forEach((a) => { - const { style, decorator } = this.generateInlineErrorDecorator(fileName, a) - editor.setDecorations(style, [decorator]) - }) + const { style, decorator } = this.generateInlineErrorDecorator(fileName, a); + editor.setDecorations(style, [decorator]); + }); } } canUpdateActiveEditor(editor: vscode.TextEditor) { - const inSettings = !editor.document + const inSettings = !editor.document; if (inSettings) { - return false + return false; } if (this.parsingTestFile) { - return false + return false; } // check if file is a possible code file: js/jsx/ts/tsx - const codeRegex = /\.[t|j]sx?$/ - return codeRegex.test(editor.document.uri.fsPath) + const codeRegex = /\.[t|j]sx?$/; + return codeRegex.test(editor.document.uri.fsPath); } public deactivate() { - this.jestProcessManager.stopAll() + this.jestProcessManager.stopAll(); } - public runTest = async (workspaceFolder: vscode.WorkspaceFolder, fileName: string, identifier: string) => { - const restart = this.jestProcessManager.numberOfProcesses > 0 - this.jestProcessManager.stopAll() + public runTest = async ( + workspaceFolder: vscode.WorkspaceFolder, + fileName: string, + identifier: string + ) => { + const restart = this.jestProcessManager.numberOfProcesses > 0; + this.jestProcessManager.stopAll(); - this.debugConfigurationProvider.prepareTestRun(fileName, identifier) + this.debugConfigurationProvider.prepareTestRun(fileName, identifier); const handle = vscode.debug.onDidTerminateDebugSession((_) => { - handle.dispose() + handle.dispose(); if (restart) { - this.startProcess() + this.startProcess(); } - }) + }); try { // try to run the debug configuration from launch.json - await vscode.debug.startDebugging(workspaceFolder, 'vscode-jest-tests') + await vscode.debug.startDebugging(workspaceFolder, 'vscode-jest-tests'); } catch { // if that fails, there (probably) isn't any debug configuration (at least no correctly named one) // therefore debug the test using the default configuration - const debugConfiguration = this.debugConfigurationProvider.provideDebugConfigurations(workspaceFolder)[0] - await vscode.debug.startDebugging(workspaceFolder, debugConfiguration) + const debugConfiguration = this.debugConfigurationProvider.provideDebugConfigurations( + workspaceFolder + )[0]; + await vscode.debug.startDebugging(workspaceFolder, debugConfiguration); } - } + }; onDidCloseTextDocument(document: vscode.TextDocument) { - this.removeCachedTestResults(document) - this.removeCachedDecorationTypes(document) + this.removeCachedTestResults(document); + this.removeCachedDecorationTypes(document); } removeCachedTestResults(document: vscode.TextDocument) { if (!document || document.isUntitled) { - return + return; } - const filePath = document.fileName - this.testResultProvider.removeCachedResults(filePath) + const filePath = document.fileName; + this.testResultProvider.removeCachedResults(filePath); } removeCachedDecorationTypes(document: vscode.TextDocument) { if (!document || !document.fileName) { - return + return; } - delete this.failingAssertionDecorators[document.fileName] + delete this.failingAssertionDecorators[document.fileName]; } onDidChangeActiveTextEditor(editor: vscode.TextEditor) { - this.triggerUpdateActiveEditor(editor) + this.triggerUpdateActiveEditor(editor); } /** @@ -313,36 +344,36 @@ export class JestExt { */ onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { if (event.document.isDirty) { - return + return; } if (event.document.uri.scheme === 'git') { - return + return; } // Ignore a clean file with a change: if (event.contentChanges.length > 0) { - return + return; } - this.removeCachedTestResults(event.document) + this.removeCachedTestResults(event.document); for (const editor of vscode.window.visibleTextEditors) { if (editor.document === event.document) { - this.triggerUpdateActiveEditor(editor) + this.triggerUpdateActiveEditor(editor); } } } toggleCoverageOverlay() { - this.coverageOverlay.toggleVisibility() + this.coverageOverlay.toggleVisibility(); // restart jest since coverage condition has changed - this.triggerUpdateSettings(this.pluginSettings) + this.triggerUpdateSettings(this.pluginSettings); } private detectedSnapshotErrors() { if (!this.pluginSettings.enableSnapshotUpdateMessages) { - return + return; } vscode.window .showInformationMessage('Would you like to update your Snapshots?', { title: 'Replace them' }) @@ -352,164 +383,175 @@ export class JestExt { this.jestProcess.runJestWithUpdateForSnapshots(() => { if (this.pluginSettings.restartJestOnSnapshotUpdate) { this.jestProcessManager.stopJestProcess(this.jestProcess).then(() => { - this.startProcess() - }) - vscode.window.showInformationMessage('Updated Snapshots and restarted Jest.') + this.startProcess(); + }); + vscode.window.showInformationMessage('Updated Snapshots and restarted Jest.'); } else { - vscode.window.showInformationMessage('Updated Snapshots. It will show in your next test run.') + vscode.window.showInformationMessage( + 'Updated Snapshots. It will show in your next test run.' + ); } - }) + }); } - }) + }); } private resetInlineErrorDecorators(editor: vscode.TextEditor) { if (!this.failingAssertionDecorators[editor.document.fileName]) { - this.failingAssertionDecorators[editor.document.fileName] = [] - return + this.failingAssertionDecorators[editor.document.fileName] = []; + return; } if (isOpenInMultipleEditors(editor.document)) { - return + return; } this.failingAssertionDecorators[editor.document.fileName].forEach((element) => { - element.dispose() - }) - this.failingAssertionDecorators[editor.document.fileName] = [] + element.dispose(); + }); + this.failingAssertionDecorators[editor.document.fileName] = []; } private generateInlineErrorDecorator(fileName: string, test: TestResult) { - const errorMessage = test.terseMessage || test.shortMessage + const errorMessage = test.terseMessage || test.shortMessage; const decorator = { range: new vscode.Range(test.lineNumberOfError, 0, test.lineNumberOfError, 0), - } + }; // We have to make a new style for each unique message, this is // why we have to remove off of them beforehand - const style = decorations.failingAssertionStyle(errorMessage) - this.failingAssertionDecorators[fileName].push(style) + const style = decorations.failingAssertionStyle(errorMessage); + this.failingAssertionDecorators[fileName].push(style); - return { style, decorator } + return { style, decorator }; } private handleStdErr(error: Buffer) { - const message = error.toString() + const message = error.toString(); if (this.shouldIgnoreOutput(message)) { - return + return; } if (isWatchNotSupported(message)) { - this.jestProcess.watchMode = WatchMode.WatchAll + this.jestProcess.watchMode = WatchMode.WatchAll; } - const noANSI = cleanAnsi(message) + const noANSI = cleanAnsi(message); if (/(snapshots? failed)|(snapshot test failed)/i.test(noANSI)) { - this.detectedSnapshotErrors() + this.detectedSnapshotErrors(); } - this.channel.appendLine(noANSI) + this.channel.appendLine(noANSI); } private handleJestEditorSupportEvent(output: string) { if (output.includes('onRunStart')) { - this.channel.clear() - this.updateStatusBar('running', 'Running tests', false) + this.channel.clear(); + this.updateStatusBar('running', 'Running tests', false); } if (output.includes('onRunComplete')) { - this.updateStatusBar('stopped', undefined, false) - this.parsingTestFile = false + this.updateStatusBar('stopped', undefined, false); + this.parsingTestFile = false; } if (!this.shouldIgnoreOutput(output)) { - this.channel.appendLine(output) + this.channel.appendLine(output); } } private assignHandlers(jestProcess: JestProcess) { jestProcess .onJestEditorSupportEvent('executableJSON', (data: JestTotalResults) => { - this.updateWithData(data) + this.updateWithData(data); }) .onJestEditorSupportEvent('executableOutput', this.handleJestEditorSupportEvent) .onJestEditorSupportEvent('executableStdErr', (error: Buffer) => this.handleStdErr(error)) .onJestEditorSupportEvent('nonTerminalError', (error: string) => { - this.channel.appendLine(`Received an error from Jest Runner: ${error.toString()}`) - this.channel.show(true) + this.channel.appendLine(`Received an error from Jest Runner: ${error.toString()}`); + this.channel.show(true); }) .onJestEditorSupportEvent('exception', (result) => { - this.channel.appendLine(`\nException raised: [${result.type}]: ${result.message}\n`) - this.channel.show(true) + this.channel.appendLine(`\nException raised: [${result.type}]: ${result.message}\n`); + this.channel.show(true); }) .onJestEditorSupportEvent('terminalError', (error: string) => { - this.channel.appendLine('\nException raised: ' + error) - this.channel.show(true) - }) + this.channel.appendLine('\nException raised: ' + error); + this.channel.show(true); + }); } private setupStatusBar() { - this.updateStatusBar('initial', undefined, false) + this.updateStatusBar('initial', undefined, false); } private updateStatusBar(status: Status, details?: string, watchMode = true) { - const modes: Mode[] = [] + const modes: Mode[] = []; if (this.coverageOverlay.enabled) { - modes.push('coverage') + modes.push('coverage'); } if (watchMode) { - modes.push('watch') + modes.push('watch'); } - this.status.update(status, details, modes) + this.status.update(status, details, modes); } private setupDecorators() { - this.passingItStyle = decorations.passingItName() - this.failingItStyle = decorations.failingItName() - this.skipItStyle = decorations.skipItName() - this.unknownItStyle = decorations.notRanItName() + this.passingItStyle = decorations.passingItName(); + this.failingItStyle = decorations.failingItName(); + this.skipItStyle = decorations.skipItName(); + this.unknownItStyle = decorations.notRanItName(); } private shouldIgnoreOutput(text: string): boolean { // this fails when snapshots change - to be revised - returning always false for now - return text.includes('Watch Usage') || text.includes('onRunComplete') || text.includes('onRunStart') + return ( + text.includes('Watch Usage') || text.includes('onRunComplete') || text.includes('onRunStart') + ); } private updateWithData(data: JestTotalResults) { - const noAnsiData = resultsWithoutAnsiEscapeSequence(data) - const normalizedData = resultsWithLowerCaseWindowsDriveLetters(noAnsiData) - this.coverageMapProvider.update(normalizedData.coverageMap) + const noAnsiData = resultsWithoutAnsiEscapeSequence(data); + const normalizedData = resultsWithLowerCaseWindowsDriveLetters(noAnsiData); + this.coverageMapProvider.update(normalizedData.coverageMap); - const statusList = this.testResultProvider.updateTestResults(normalizedData) - updateDiagnostics(statusList, this.failDiagnostics) + const statusList = this.testResultProvider.updateTestResults(normalizedData); + updateDiagnostics(statusList, this.failDiagnostics); - const failedFileCount = failedSuiteCount(this.failDiagnostics) + const failedFileCount = failedSuiteCount(this.failDiagnostics); if (failedFileCount <= 0 && normalizedData.success) { - this.updateStatusBar('success') + this.updateStatusBar('success'); } else { - this.updateStatusBar('failed', ` (${failedFileCount} test suite${failedFileCount > 1 ? 's' : ''} failed)`) + this.updateStatusBar( + 'failed', + ` (${failedFileCount} test suite${failedFileCount > 1 ? 's' : ''} failed)` + ); } for (const editor of vscode.window.visibleTextEditors) { if (vscode.workspace.getWorkspaceFolder(editor.document.uri) === this.workspaceFolder) { - this.triggerUpdateActiveEditor(editor) + this.triggerUpdateActiveEditor(editor); } } } - private generateDotsForItBlocks(blocks: TestResult[], state: TestReconciliationState): DecorationOptions[] { + private generateDotsForItBlocks( + blocks: TestResult[], + state: TestReconciliationState + ): DecorationOptions[] { const nameForState = { [TestReconciliationState.KnownSuccess]: 'Passed', [TestReconciliationState.KnownFail]: 'Failed', [TestReconciliationState.KnownSkip]: 'Skipped', - [TestReconciliationState.Unknown]: 'Test has not run yet, due to Jest only running tests related to changes.', - } + [TestReconciliationState.Unknown]: + 'Test has not run yet, due to Jest only running tests related to changes.', + }; return blocks.map((it) => { return { range: new vscode.Range(it.start.line, it.start.column, it.start.line, it.start.column + 1), hoverMessage: nameForState[state], identifier: it.name, - } - }) + }; + }); } } diff --git a/src/JestProcessManagement/JestProcess.ts b/src/JestProcessManagement/JestProcess.ts index 53b2f129f..5a4ba6d33 100644 --- a/src/JestProcessManagement/JestProcess.ts +++ b/src/JestProcessManagement/JestProcess.ts @@ -1,110 +1,110 @@ -import * as vscode from 'vscode' -import { join } from 'path' -import { Runner, ProjectWorkspace } from 'jest-editor-support' -import { WatchMode } from '../Jest' -import { ExitCallback } from './JestProcessManager' -import { extensionId } from '../appGlobals' +import * as vscode from 'vscode'; +import { join } from 'path'; +import { Runner, ProjectWorkspace } from 'jest-editor-support'; +import { WatchMode } from '../Jest'; +import { ExitCallback } from './JestProcessManager'; +import { extensionId } from '../appGlobals'; export class JestProcess { - static readonly keepAliveLimit = 5 - static readonly stopHangTimeout = 500 - public keepAlive: boolean - public watchMode: WatchMode - private runner: Runner - private projectWorkspace: ProjectWorkspace - private onExitCallback: ExitCallback - private jestSupportEvents: Map<string, (...args: any[]) => void> - private stopResolveCallback: () => void | null - private keepAliveCounter: number + static readonly keepAliveLimit = 5; + static readonly stopHangTimeout = 500; + public keepAlive: boolean; + public watchMode: WatchMode; + private runner: Runner; + private projectWorkspace: ProjectWorkspace; + private onExitCallback: ExitCallback; + private jestSupportEvents: Map<string, (...args: any[]) => void>; + private stopResolveCallback: () => void | null; + private keepAliveCounter: number; constructor({ projectWorkspace, watchMode = WatchMode.None, keepAlive = false, }: { - projectWorkspace: ProjectWorkspace - watchMode?: WatchMode - keepAlive?: boolean + projectWorkspace: ProjectWorkspace; + watchMode?: WatchMode; + keepAlive?: boolean; }) { - this.keepAlive = keepAlive - this.watchMode = watchMode - this.projectWorkspace = projectWorkspace - this.keepAliveCounter = keepAlive ? JestProcess.keepAliveLimit : 1 - this.jestSupportEvents = new Map() + this.keepAlive = keepAlive; + this.watchMode = watchMode; + this.projectWorkspace = projectWorkspace; + this.keepAliveCounter = keepAlive ? JestProcess.keepAliveLimit : 1; + this.jestSupportEvents = new Map(); - this.startRunner() + this.startRunner(); } public onExit(callback: ExitCallback) { - this.onExitCallback = callback + this.onExitCallback = callback; } public onJestEditorSupportEvent(event: string, callback: (...args: any[]) => void) { - this.jestSupportEvents.set(event, callback) - this.runner.on(event, callback) - return this + this.jestSupportEvents.set(event, callback); + this.runner.on(event, callback); + return this; } public stop(): Promise<void> { return new Promise((resolve) => { - this.keepAliveCounter = 1 - this.stopResolveCallback = resolve - this.jestSupportEvents.clear() - this.runner.closeProcess() + this.keepAliveCounter = 1; + this.stopResolveCallback = resolve; + this.jestSupportEvents.clear(); + this.runner.closeProcess(); // As a safety fallback to prevent the stop from hanging, resolve after a timeout // this is safe since subsequent resolve calls are no-op // TODO: If `closeProcess` can be guarenteed to always resolve, remove this - setTimeout(resolve, JestProcess.stopHangTimeout) - }) + setTimeout(resolve, JestProcess.stopHangTimeout); + }); } public runJestWithUpdateForSnapshots(callback: () => void) { - this.runner.runJestWithUpdateForSnapshots(callback) + this.runner.runJestWithUpdateForSnapshots(callback); } public stopRequested(): boolean { - return this.stopResolveCallback !== null + return this.stopResolveCallback !== null; } private startRunner() { - this.stopResolveCallback = null - let exited = false + this.stopResolveCallback = null; + let exited = false; - const extensionPath = vscode.extensions.getExtension(extensionId).extensionPath - const reporterPath = join(extensionPath, 'out', 'reporter.js') + const extensionPath = vscode.extensions.getExtension(extensionId).extensionPath; + const reporterPath = join(extensionPath, 'out', 'reporter.js'); const options = { noColor: true, reporters: ['default', `"${reporterPath}"`], - } - this.runner = new Runner(this.projectWorkspace, options) + }; + this.runner = new Runner(this.projectWorkspace, options); - this.restoreJestEvents() + this.restoreJestEvents(); - this.runner.start(this.watchMode !== WatchMode.None, this.watchMode === WatchMode.WatchAll) + this.runner.start(this.watchMode !== WatchMode.None, this.watchMode === WatchMode.WatchAll); this.runner.on('debuggerProcessExit', () => { if (!exited) { - exited = true + exited = true; if (--this.keepAliveCounter > 0) { - this.runner.removeAllListeners() - this.startRunner() + this.runner.removeAllListeners(); + this.startRunner(); } else { if (this.onExitCallback) { - this.onExitCallback(this) + this.onExitCallback(this); } if (this.stopResolveCallback) { - this.stopResolveCallback() - this.stopResolveCallback = null + this.stopResolveCallback(); + this.stopResolveCallback = null; } } } - }) + }); } private restoreJestEvents() { for (const [event, callback] of this.jestSupportEvents.entries()) { - this.runner.on(event, callback) + this.runner.on(event, callback); } } } diff --git a/src/JestProcessManagement/JestProcessManager.ts b/src/JestProcessManagement/JestProcessManager.ts index 189fdae1b..625055b2b 100644 --- a/src/JestProcessManagement/JestProcessManager.ts +++ b/src/JestProcessManagement/JestProcessManager.ts @@ -1,23 +1,26 @@ -import { ProjectWorkspace } from 'jest-editor-support' -import { JestProcess } from './JestProcess' -import { WatchMode } from '../Jest' +import { ProjectWorkspace } from 'jest-editor-support'; +import { JestProcess } from './JestProcess'; +import { WatchMode } from '../Jest'; -export type ExitCallback = (exitedJestProcess: JestProcess, jestProcessInWatchMode?: JestProcess) => void +export type ExitCallback = ( + exitedJestProcess: JestProcess, + jestProcessInWatchMode?: JestProcess +) => void; export class JestProcessManager { - private projectWorkspace: ProjectWorkspace - private jestProcesses: JestProcess[] = [] - private runAllTestsFirstInWatchMode: boolean + private projectWorkspace: ProjectWorkspace; + private jestProcesses: JestProcess[] = []; + private runAllTestsFirstInWatchMode: boolean; constructor({ projectWorkspace, runAllTestsFirstInWatchMode = true, }: { - projectWorkspace: ProjectWorkspace - runAllTestsFirstInWatchMode?: boolean + projectWorkspace: ProjectWorkspace; + runAllTestsFirstInWatchMode?: boolean; }) { - this.projectWorkspace = projectWorkspace - this.runAllTestsFirstInWatchMode = runAllTestsFirstInWatchMode + this.projectWorkspace = projectWorkspace; + this.runAllTestsFirstInWatchMode = runAllTestsFirstInWatchMode; } public startJestProcess({ @@ -27,51 +30,51 @@ export class JestProcessManager { watchMode = WatchMode.None, keepAlive = false, }: { - exitCallback?: ExitCallback - watchMode?: WatchMode - keepAlive?: boolean + exitCallback?: ExitCallback; + watchMode?: WatchMode; + keepAlive?: boolean; } = {}): JestProcess { if (watchMode !== WatchMode.None && this.runAllTestsFirstInWatchMode) { return this.runAllTestsFirst((exitedJestProcess) => { // cancel the rest execution if stop() has been requested. if (exitedJestProcess.stopRequested()) { - return + return; } - this.removeJestProcessReference(exitedJestProcess) + this.removeJestProcessReference(exitedJestProcess); const jestProcessInWatchMode = this.run({ watchMode: WatchMode.Watch, keepAlive, exitCallback, - }) - exitCallback(exitedJestProcess, jestProcessInWatchMode) - }) + }); + exitCallback(exitedJestProcess, jestProcessInWatchMode); + }); } else { return this.run({ watchMode, keepAlive, exitCallback, - }) + }); } } public stopAll() { - const processesToRemove = [...this.jestProcesses] - this.jestProcesses = [] - return Promise.all(processesToRemove.map((jestProcess) => jestProcess.stop())) + const processesToRemove = [...this.jestProcesses]; + this.jestProcesses = []; + return Promise.all(processesToRemove.map((jestProcess) => jestProcess.stop())); } public stopJestProcess(jestProcess: JestProcess) { - this.removeJestProcessReference(jestProcess) - return jestProcess.stop() + this.removeJestProcessReference(jestProcess); + return jestProcess.stop(); } public get numberOfProcesses() { - return this.jestProcesses.length + return this.jestProcesses.length; } private removeJestProcessReference(jestProcess: JestProcess) { - const index = this.jestProcesses.indexOf(jestProcess) + const index = this.jestProcesses.indexOf(jestProcess); if (index !== -1) { - this.jestProcesses.splice(index, 1) + this.jestProcesses.splice(index, 1); } } @@ -80,20 +83,20 @@ export class JestProcessManager { keepAlive, exitCallback, }: { - watchMode: WatchMode - keepAlive: boolean - exitCallback: ExitCallback + watchMode: WatchMode; + keepAlive: boolean; + exitCallback: ExitCallback; }) { const jestProcess = new JestProcess({ projectWorkspace: this.projectWorkspace, watchMode, keepAlive, - }) + }); - this.jestProcesses.unshift(jestProcess) + this.jestProcesses.unshift(jestProcess); - jestProcess.onExit(exitCallback) - return jestProcess + jestProcess.onExit(exitCallback); + return jestProcess; } private run({ @@ -101,20 +104,20 @@ export class JestProcessManager { keepAlive, exitCallback, }: { - watchMode: WatchMode - keepAlive: boolean - exitCallback: ExitCallback + watchMode: WatchMode; + keepAlive: boolean; + exitCallback: ExitCallback; }) { return this.runJest({ watchMode, keepAlive, exitCallback: (exitedJestProcess: JestProcess) => { - exitCallback(exitedJestProcess) + exitCallback(exitedJestProcess); if (!exitedJestProcess.keepAlive) { - this.removeJestProcessReference(exitedJestProcess) + this.removeJestProcessReference(exitedJestProcess); } }, - }) + }); } private runAllTestsFirst(onExit: ExitCallback) { @@ -122,6 +125,6 @@ export class JestProcessManager { watchMode: WatchMode.None, keepAlive: false, exitCallback: onExit, - }) + }); } } diff --git a/src/JestProcessManagement/index.ts b/src/JestProcessManagement/index.ts index 8adbffd12..17e8723d6 100644 --- a/src/JestProcessManagement/index.ts +++ b/src/JestProcessManagement/index.ts @@ -1,2 +1,2 @@ -export { JestProcess } from './JestProcess' -export { JestProcessManager } from './JestProcessManager' +export { JestProcess } from './JestProcess'; +export { JestProcessManager } from './JestProcessManager'; diff --git a/src/Settings/index.ts b/src/Settings/index.ts index 4e3895b2c..4578d73d0 100644 --- a/src/Settings/index.ts +++ b/src/Settings/index.ts @@ -1,32 +1,32 @@ -import { TestState } from '../DebugCodeLens' +import { TestState } from '../DebugCodeLens'; export interface PluginResourceSettings { - autoEnable?: boolean - enableInlineErrorMessages?: boolean - enableSnapshotUpdateMessages?: boolean - pathToConfig?: string - pathToJest?: string - restartJestOnSnapshotUpdate?: boolean - rootPath?: string - runAllTestsFirst?: boolean - showCoverageOnLoad: boolean - coverageFormatter: string - debugMode?: boolean + autoEnable?: boolean; + enableInlineErrorMessages?: boolean; + enableSnapshotUpdateMessages?: boolean; + pathToConfig?: string; + pathToJest?: string; + restartJestOnSnapshotUpdate?: boolean; + rootPath?: string; + runAllTestsFirst?: boolean; + showCoverageOnLoad: boolean; + coverageFormatter: string; + debugMode?: boolean; } export interface PluginWindowSettings { debugCodeLens: { - enabled: boolean - showWhenTestStateIn: TestState[] - } - enableSnapshotPreviews?: boolean - disabledWorkspaceFolders: string[] + enabled: boolean; + showWhenTestStateIn: TestState[]; + }; + enableSnapshotPreviews?: boolean; + disabledWorkspaceFolders: string[]; } export function isDefaultPathToJest(str) { - return str === null || str === '' + return str === null || str === ''; } export function hasUserSetPathToJest(str) { - return !isDefaultPathToJest(str) + return !isDefaultPathToJest(str); } diff --git a/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts b/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts index fead796a3..52bafbf09 100644 --- a/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts +++ b/src/SnapshotCodeLens/SnapshotCodeLensProvider.ts @@ -1,44 +1,47 @@ -import * as vscode from 'vscode' -import { Snapshot } from 'jest-editor-support' +import * as vscode from 'vscode'; +import { Snapshot } from 'jest-editor-support'; -import { extensionName } from '../appGlobals' -import { previewCommand } from './SnapshotPreviewProvider' +import { extensionName } from '../appGlobals'; +import { previewCommand } from './SnapshotPreviewProvider'; -const missingSnapshotCommand = `${extensionName}.snapshot.missing` +const missingSnapshotCommand = `${extensionName}.snapshot.missing`; class SnapshotCodeLensProvider implements vscode.CodeLensProvider { public provideCodeLenses(document: vscode.TextDocument, _token: vscode.CancellationToken) { - const snapshots = new Snapshot() + const snapshots = new Snapshot(); return snapshots.getMetadata(document.uri.fsPath).map((snapshot) => { - const { line } = snapshot.node.loc.start - const range = new vscode.Range(line - 1, 0, line - 1, 0) - let command: vscode.Command + const { line } = snapshot.node.loc.start; + const range = new vscode.Range(line - 1, 0, line - 1, 0); + let command: vscode.Command; if (snapshot.exists) { command = { title: 'view snapshot', command: previewCommand, arguments: [snapshot], - } + }; } else { command = { title: 'snapshot missing', command: missingSnapshotCommand, - } + }; } - return new vscode.CodeLens(range, command) - }) + return new vscode.CodeLens(range, command); + }); } } export function registerSnapshotCodeLens(enableSnapshotPreviews: boolean) { if (!enableSnapshotPreviews) { - return [] + return []; } return [ - vscode.languages.registerCodeLensProvider({ pattern: '**/*.{ts,tsx,js,jsx}' }, new SnapshotCodeLensProvider()), + vscode.languages.registerCodeLensProvider( + { pattern: '**/*.{ts,tsx,js,jsx}' }, + new SnapshotCodeLensProvider() + ), vscode.commands.registerCommand(missingSnapshotCommand, () => { - vscode.window.showInformationMessage('Run test to generate snapshot.') + vscode.window.showInformationMessage('Run test to generate snapshot.'); }), - ] + ]; } diff --git a/src/SnapshotCodeLens/SnapshotPreviewProvider.ts b/src/SnapshotCodeLens/SnapshotPreviewProvider.ts index 303545100..c0dc542b9 100644 --- a/src/SnapshotCodeLens/SnapshotPreviewProvider.ts +++ b/src/SnapshotCodeLens/SnapshotPreviewProvider.ts @@ -1,12 +1,12 @@ -import * as vscode from 'vscode' -import { SnapshotMetadata } from 'jest-editor-support' +import * as vscode from 'vscode'; +import { SnapshotMetadata } from 'jest-editor-support'; -import { extensionName } from '../appGlobals' +import { extensionName } from '../appGlobals'; -export const previewCommand = `${extensionName}.snapshot.preview` +export const previewCommand = `${extensionName}.snapshot.preview`; export function registerSnapshotPreview() { - let panel: vscode.WebviewPanel = null + let panel: vscode.WebviewPanel = null; const escaped = (snapshot: string) => { if (snapshot) { @@ -16,25 +16,30 @@ export function registerSnapshotPreview() { .replace(/"/g, '"') .replace(/'/g, ''') .replace(/</g, '<') - .replace(/>/g, '>') - return `<pre>${escaped}</pre>` + .replace(/>/g, '>'); + return `<pre>${escaped}</pre>`; } - } + }; return [ vscode.commands.registerCommand(previewCommand, (snapshot: SnapshotMetadata) => { if (panel) { - panel.reveal() + panel.reveal(); } else { - panel = vscode.window.createWebviewPanel('view_snapshot', snapshot.name, vscode.ViewColumn.Two, {}) + panel = vscode.window.createWebviewPanel( + 'view_snapshot', + snapshot.name, + vscode.ViewColumn.Two, + {} + ); panel.onDidDispose(() => { - panel = null - }) + panel = null; + }); } - panel.webview.html = escaped(snapshot.content) - panel.title = snapshot.name + panel.webview.html = escaped(snapshot.content); + panel.title = snapshot.name; }), - ] + ]; } diff --git a/src/SnapshotCodeLens/index.ts b/src/SnapshotCodeLens/index.ts index 285892efb..4bc6a8689 100644 --- a/src/SnapshotCodeLens/index.ts +++ b/src/SnapshotCodeLens/index.ts @@ -1,2 +1,2 @@ -export { registerSnapshotCodeLens } from './SnapshotCodeLensProvider' -export { registerSnapshotPreview } from './SnapshotPreviewProvider' +export { registerSnapshotCodeLens } from './SnapshotCodeLensProvider'; +export { registerSnapshotPreview } from './SnapshotPreviewProvider'; diff --git a/src/StatusBar.ts b/src/StatusBar.ts index e95ef00ed..6936e422f 100644 --- a/src/StatusBar.ts +++ b/src/StatusBar.ts @@ -1,108 +1,109 @@ -import * as vscode from 'vscode' -import { extensionName } from './appGlobals' -import { JestExt } from './JestExt' +import * as vscode from 'vscode'; +import { extensionName } from './appGlobals'; +import { JestExt } from './JestExt'; export enum StatusType { active, summary, } -export type Status = 'running' | 'failed' | 'success' | 'stopped' | 'initial' -export type Mode = 'watch' | 'coverage' +export type Status = 'running' | 'failed' | 'success' | 'stopped' | 'initial'; +export type Mode = 'watch' | 'coverage'; interface StatusUpdateRequest { - source: string - status: Status - details?: string - modes?: Mode[] + source: string; + status: Status; + details?: string; + modes?: Mode[]; } -interface SpinnableStatusBarItem extends Pick<vscode.StatusBarItem, 'command' | 'text' | 'tooltip'> { - readonly type: StatusType - show(): void - hide(): void +interface SpinnableStatusBarItem + extends Pick<vscode.StatusBarItem, 'command' | 'text' | 'tooltip'> { + readonly type: StatusType; + show(): void; + hide(): void; } const createStatusBarItem = (type: StatusType, priority: number): SpinnableStatusBarItem => { - const item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, priority) + const item = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, priority); return { type, show: () => item.show(), hide: () => item.hide(), get command() { - return item.command + return item.command; }, get text() { - return item.text + return item.text; }, get tooltip() { - return item.tooltip + return item.tooltip; }, set command(_command) { - item.command = _command + item.command = _command; }, set text(_text: string) { - item.text = _text + item.text = _text; }, set tooltip(_tooltip: string) { - item.tooltip = _tooltip + item.tooltip = _tooltip; }, - } -} + }; +}; // The bottom status bar export class StatusBar { - private activeStatusItem = createStatusBarItem(StatusType.active, 2) - private summaryStatusItem = createStatusBarItem(StatusType.summary, 1) + private activeStatusItem = createStatusBarItem(StatusType.active, 2); + private summaryStatusItem = createStatusBarItem(StatusType.summary, 1); - private priorities: Status[] = ['running', 'failed', 'success', 'stopped', 'initial'] - private requests = new Map<string, StatusUpdateRequest>() - private _activeFolder?: string - private summaryOutput?: vscode.OutputChannel + private priorities: Status[] = ['running', 'failed', 'success', 'stopped', 'initial']; + private requests = new Map<string, StatusUpdateRequest>(); + private _activeFolder?: string; + private summaryOutput?: vscode.OutputChannel; constructor() { - this.summaryStatusItem.tooltip = 'Jest status summary of the workspace' - this.activeStatusItem.tooltip = 'Jest status of the active folder' + this.summaryStatusItem.tooltip = 'Jest status summary of the workspace'; + this.activeStatusItem.tooltip = 'Jest status of the active folder'; } register(getExtension: (name: string) => JestExt | undefined) { - const showSummaryOutput = `${extensionName}.show-summary-output` - const showActiveOutput = `${extensionName}.show-active-output` - this.summaryStatusItem.command = showSummaryOutput - this.activeStatusItem.command = showActiveOutput + const showSummaryOutput = `${extensionName}.show-summary-output`; + const showActiveOutput = `${extensionName}.show-active-output`; + this.summaryStatusItem.command = showSummaryOutput; + this.activeStatusItem.command = showActiveOutput; return [ vscode.commands.registerCommand(showSummaryOutput, () => { if (this.summaryOutput) { - this.summaryOutput.show() + this.summaryOutput.show(); } }), vscode.commands.registerCommand(showActiveOutput, () => { if (this.activeFolder) { - const ext = getExtension(this.activeFolder) + const ext = getExtension(this.activeFolder); if (ext) { - ext.channel.show() + ext.channel.show(); } } }), - ] + ]; } bind(source: string) { return { update: (status: Status, details?: string, modes?: Mode[]) => { - this.request(source, status, details, modes) + this.request(source, status, details, modes); }, - } + }; } onDidChangeActiveTextEditor(editor: vscode.TextEditor) { if (editor && editor.document) { - const folder = vscode.workspace.getWorkspaceFolder(editor.document.uri) + const folder = vscode.workspace.getWorkspaceFolder(editor.document.uri); if (folder && folder.name !== this._activeFolder) { - this._activeFolder = folder.name - this.updateActiveStatus() + this._activeFolder = folder.name; + this.updateActiveStatus(); } } } @@ -113,146 +114,148 @@ export class StatusBar { status, details, modes, - } - this.requests.set(source, request) - this.updateStatus(request) + }; + this.requests.set(source, request); + this.updateStatus(request); } private updateStatus(request: StatusUpdateRequest) { - this.updateActiveStatus(request) - this.updateSummaryStatus() + this.updateActiveStatus(request); + this.updateSummaryStatus(); } private get activeFolder() { if (!this._activeFolder) { if (vscode.workspace.workspaceFolders.length === 1) { // there's only one workspaceFolder, so let's take it - this._activeFolder = vscode.workspace.workspaceFolders[0].name + this._activeFolder = vscode.workspace.workspaceFolders[0].name; } else if (vscode.window.activeTextEditor) { // otherwise select correct workspaceFolder based on the currently open textEditor - const folder = vscode.workspace.getWorkspaceFolder(vscode.window.activeTextEditor.document.uri) + const folder = vscode.workspace.getWorkspaceFolder( + vscode.window.activeTextEditor.document.uri + ); if (folder) { - this._activeFolder = folder.name + this._activeFolder = folder.name; } } } - return this._activeFolder + return this._activeFolder; } private updateActiveStatus(request?: StatusUpdateRequest) { if (request && this.activeFolder) { if (request.source === this.activeFolder) { - this.render(request, this.activeStatusItem) + this.render(request, this.activeStatusItem); } - return + return; } // find the active item from requests - let _request = null + let _request = null; if (this.activeFolder) { - _request = this.requests.get(this.activeFolder) + _request = this.requests.get(this.activeFolder); } if (!_request && this.requests.size === 1) { - _request = this.requests.values().next().value + _request = this.requests.values().next().value; } if (_request) { - this.render(_request, this.activeStatusItem) + this.render(_request, this.activeStatusItem); } else { - this.activeStatusItem.hide() + this.activeStatusItem.hide(); } } private updateSummaryStatus() { if (this.needsSummaryStatus()) { - this.updateSummaryOutput() + this.updateSummaryOutput(); - let summaryStatus: StatusUpdateRequest | undefined - let prev = 99 + let summaryStatus: StatusUpdateRequest | undefined; + let prev = 99; for (const r of this.requests.values()) { - const idx = this.priorities.indexOf(r.status) + const idx = this.priorities.indexOf(r.status); if (idx >= 0 && idx < prev) { - summaryStatus = r - prev = idx + summaryStatus = r; + prev = idx; } } if (summaryStatus) { - this.render(summaryStatus, this.summaryStatusItem) - return + this.render(summaryStatus, this.summaryStatusItem); + return; } } - this.summaryStatusItem.hide() + this.summaryStatusItem.hide(); } private render(request: StatusUpdateRequest, statusBarItem: SpinnableStatusBarItem) { - const message = this.getMessageByStatus(request.status) + const message = this.getMessageByStatus(request.status); switch (statusBarItem.type) { case StatusType.active: { - const modes = this.getModes(request.modes) - const details = !this.needsSummaryStatus() && request.details ? request.details : '' - const displayString = [message, details, modes].filter((s) => s && s.length > 0).join(' ') - statusBarItem.text = `Jest: ${displayString}` - statusBarItem.tooltip = `Jest status of '${this.activeFolder}'` - break + const modes = this.getModes(request.modes); + const details = !this.needsSummaryStatus() && request.details ? request.details : ''; + const displayString = [message, details, modes].filter((s) => s && s.length > 0).join(' '); + statusBarItem.text = `Jest: ${displayString}`; + statusBarItem.tooltip = `Jest status of '${this.activeFolder}'`; + break; } case StatusType.summary: - statusBarItem.text = `Jest-WS: ${message}` - break + statusBarItem.text = `Jest-WS: ${message}`; + break; default: - throw new Error(`unexpected statusType: ${statusBarItem.type}`) + throw new Error(`unexpected statusType: ${statusBarItem.type}`); } - statusBarItem.show() + statusBarItem.show(); } private updateSummaryOutput() { if (!this.summaryOutput) { - this.summaryOutput = vscode.window.createOutputChannel('Jest (Workspace)') + this.summaryOutput = vscode.window.createOutputChannel('Jest (Workspace)'); } - this.summaryOutput.clear() + this.summaryOutput.clear(); - const messages = [] + const messages = []; this.requests.forEach((item) => { - const details = item.details ? `: ${item.details}` : '' - messages.push(`${item.source}: ${item.status} ${details}`) - }) - this.summaryOutput.append(messages.join('\n')) + const details = item.details ? `: ${item.details}` : ''; + messages.push(`${item.source}: ${item.status} ${details}`); + }); + this.summaryOutput.append(messages.join('\n')); } private needsSummaryStatus() { - return this.requests.size > 1 + return this.requests.size > 1; } private getMessageByStatus(status: Status) { switch (status) { case 'running': - return '$(sync~spin)' + return '$(sync~spin)'; case 'failed': - return '$(alert)' + return '$(alert)'; case 'success': - return '$(check)' + return '$(check)'; case 'initial': - return '...' + return '...'; default: - return status + return status; } } private getModes(modes?: Mode[]) { if (!modes) { - return '' + return ''; } const modesStrings = modes.map((m) => { switch (m) { case 'coverage': - return '$(color-mode)' + return '$(color-mode)'; case 'watch': - return '$(eye)' + return '$(eye)'; default: - throw new Error(`unrecognized mode: ${m}`) + throw new Error(`unrecognized mode: ${m}`); } - }) - return modesStrings.join(' ') + }); + return modesStrings.join(' '); } } -export const statusBar = new StatusBar() +export const statusBar = new StatusBar(); diff --git a/src/TestParser.ts b/src/TestParser.ts index 054448df8..e6d3d037d 100644 --- a/src/TestParser.ts +++ b/src/TestParser.ts @@ -1,5 +1,5 @@ -import { IParseResults, parse } from 'jest-editor-support' +import { IParseResults, parse } from 'jest-editor-support'; export function parseTest(filePath: string): IParseResults { - return parse(filePath) + return parse(filePath); } diff --git a/src/TestResults/TestReconciliationState.ts b/src/TestResults/TestReconciliationState.ts index 02a58804d..d704dd9a9 100644 --- a/src/TestResults/TestReconciliationState.ts +++ b/src/TestResults/TestReconciliationState.ts @@ -1,4 +1,4 @@ -export type TestReconciliationState = 'Unknown' | 'KnownSuccess' | 'KnownFail' | 'KnownSkip' +export type TestReconciliationState = 'Unknown' | 'KnownSuccess' | 'KnownFail' | 'KnownSkip'; // tslint:disable-next-line variable-name export const TestReconciliationState = { @@ -6,4 +6,4 @@ export const TestReconciliationState = { KnownSuccess: 'KnownSuccess' as TestReconciliationState, KnownFail: 'KnownFail' as TestReconciliationState, KnownSkip: 'KnownSkip' as TestReconciliationState, -} +}; diff --git a/src/TestResults/TestResult.ts b/src/TestResults/TestResult.ts index 175aa3aa0..8d2eeab7e 100644 --- a/src/TestResults/TestResult.ts +++ b/src/TestResults/TestResult.ts @@ -1,83 +1,85 @@ -import { TestReconciliationState } from './TestReconciliationState' -import { JestFileResults, JestTotalResults } from 'jest-editor-support' -import { FileCoverage } from 'istanbul-lib-coverage' -import * as path from 'path' -import { cleanAnsi } from '../helpers' +import { TestReconciliationState } from './TestReconciliationState'; +import { JestFileResults, JestTotalResults } from 'jest-editor-support'; +import { FileCoverage } from 'istanbul-lib-coverage'; +import * as path from 'path'; +import { cleanAnsi } from '../helpers'; interface Position { /** Zero-based column number */ - column: number + column: number; /** Zero-based line number */ - line: number + line: number; } export interface TestResult { - name: string - start: Position - end: Position + name: string; + start: Position; + end: Position; - status: TestReconciliationState - shortMessage?: string - terseMessage?: string + status: TestReconciliationState; + shortMessage?: string; + terseMessage?: string; /** Zero-based line number */ - lineNumberOfError?: number + lineNumberOfError?: number; } export const withLowerCaseWindowsDriveLetter = (filePath: string): string | undefined => { - const match = filePath.match(/^([A-Z]:\\)(.*)$/) + const match = filePath.match(/^([A-Z]:\\)(.*)$/); if (match) { - return `${match[1].toLowerCase()}${match[2]}` + return `${match[1].toLowerCase()}${match[2]}`; } -} +}; function testResultWithLowerCaseWindowsDriveLetter(testResult: JestFileResults): JestFileResults { - const newFilePath = withLowerCaseWindowsDriveLetter(testResult.name) + const newFilePath = withLowerCaseWindowsDriveLetter(testResult.name); if (newFilePath) { return { ...testResult, name: newFilePath, - } + }; } - return testResult + return testResult; } -export const testResultsWithLowerCaseWindowsDriveLetters = (testResults: JestFileResults[]): JestFileResults[] => { +export const testResultsWithLowerCaseWindowsDriveLetters = ( + testResults: JestFileResults[] +): JestFileResults[] => { if (!testResults) { - return testResults + return testResults; } - return testResults.map(testResultWithLowerCaseWindowsDriveLetter) -} + return testResults.map(testResultWithLowerCaseWindowsDriveLetter); +}; function fileCoverageWithLowerCaseWindowsDriveLetter(fileCoverage: FileCoverage) { - const newFilePath = withLowerCaseWindowsDriveLetter(fileCoverage.path) + const newFilePath = withLowerCaseWindowsDriveLetter(fileCoverage.path); if (newFilePath) { return { ...fileCoverage, path: newFilePath, - } + }; } - return fileCoverage + return fileCoverage; } export const coverageMapWithLowerCaseWindowsDriveLetters = (data: JestTotalResults) => { if (!data.coverageMap) { - return + return; } - const result = {} - const filePaths = Object.keys(data.coverageMap) + const result = {}; + const filePaths = Object.keys(data.coverageMap); for (const filePath of filePaths) { - const newFileCoverage = fileCoverageWithLowerCaseWindowsDriveLetter(data.coverageMap[filePath]) - result[newFileCoverage.path] = newFileCoverage + const newFileCoverage = fileCoverageWithLowerCaseWindowsDriveLetter(data.coverageMap[filePath]); + result[newFileCoverage.path] = newFileCoverage; } - return result -} + return result; +}; /** * Normalize file paths on Windows systems to use lowercase drive letters. @@ -92,18 +94,18 @@ export const resultsWithLowerCaseWindowsDriveLetters = (data: JestTotalResults) ...data, coverageMap: coverageMapWithLowerCaseWindowsDriveLetters(data), testResults: testResultsWithLowerCaseWindowsDriveLetters(data.testResults), - } + }; } - return data -} + return data; +}; /** * Removes ANSI escape sequence characters from test results in order to get clean messages */ export const resultsWithoutAnsiEscapeSequence = (data: JestTotalResults) => { if (!data || !data.testResults) { - return data + return data; } return { @@ -116,5 +118,5 @@ export const resultsWithoutAnsiEscapeSequence = (data: JestTotalResults) => { failureMessages: assertion.failureMessages.map((message) => cleanAnsi(message)), })), })), - } -} + }; +}; diff --git a/src/TestResults/TestResultProvider.ts b/src/TestResults/TestResultProvider.ts index da6aca1b0..235f865f7 100644 --- a/src/TestResults/TestResultProvider.ts +++ b/src/TestResults/TestResultProvider.ts @@ -4,44 +4,44 @@ import { TestAssertionStatus, TestFileAssertionStatus, ItBlock, -} from 'jest-editor-support' -import { TestReconciliationState } from './TestReconciliationState' -import { TestResult } from './TestResult' -import { parseTest } from '../TestParser' +} from 'jest-editor-support'; +import { TestReconciliationState } from './TestReconciliationState'; +import { TestResult } from './TestResult'; +import { parseTest } from '../TestParser'; interface TestResultsMap { - [filePath: string]: TestResult[] + [filePath: string]: TestResult[]; } export interface SortedTestResults { - fail: TestResult[] - skip: TestResult[] - success: TestResult[] - unknown: TestResult[] + fail: TestResult[]; + skip: TestResult[]; + success: TestResult[]; + unknown: TestResult[]; } interface SortedTestResultsMap { - [filePath: string]: SortedTestResults + [filePath: string]: SortedTestResults; } -type IsMatched = (test: ItBlock, assertion: TestAssertionStatus) => boolean -type OnMatchError = (test: ItBlock, matched: TestAssertionStatus[]) => string | undefined +type IsMatched = (test: ItBlock, assertion: TestAssertionStatus) => boolean; +type OnMatchError = (test: ItBlock, matched: TestAssertionStatus[]) => string | undefined; export class TestResultProvider { - verbose: boolean - private reconciler: TestReconciler - private resultsByFilePath: TestResultsMap - private sortedResultsByFilePath: SortedTestResultsMap + verbose: boolean; + private reconciler: TestReconciler; + private resultsByFilePath: TestResultsMap; + private sortedResultsByFilePath: SortedTestResultsMap; constructor(verbose = false) { - this.reconciler = new TestReconciler() - this.resetCache() - this.verbose = verbose + this.reconciler = new TestReconciler(); + this.resetCache(); + this.verbose = verbose; } resetCache() { - this.resultsByFilePath = {} - this.sortedResultsByFilePath = {} + this.resultsByFilePath = {}; + this.sortedResultsByFilePath = {}; } getResults(filePath: string): TestResult[] { @@ -61,10 +61,13 @@ export class TestResultProvider { shortMessage: assertion ? assertion.shortMessage : err, terseMessage: assertion ? assertion.terseMessage : undefined, lineNumberOfError: - assertion && assertion.line && assertion.line >= test.start.line && assertion.line <= test.end.line + assertion && + assertion.line && + assertion.line >= test.start.line && + assertion.line <= test.end.line ? assertion.line - 1 : test.end.line - 1, - }) + }); const matchTests = ( _itBlocks: ItBlock[], @@ -73,77 +76,78 @@ export class TestResultProvider { _onMatchError?: OnMatchError, trackRemaining?: boolean ): [TestResult[], ItBlock[], TestAssertionStatus[]] => { - const results: TestResult[] = [] - const remainingAssertions = Array.from(_assertions) - const remainingTests: ItBlock[] = [] - const _trackRemaining = trackRemaining === undefined ? true : trackRemaining + const results: TestResult[] = []; + const remainingAssertions = Array.from(_assertions); + const remainingTests: ItBlock[] = []; + const _trackRemaining = trackRemaining === undefined ? true : trackRemaining; _itBlocks.forEach((test) => { - const matched = remainingAssertions.filter((a) => _isMatched.every((m) => m(test, a))) + const matched = remainingAssertions.filter((a) => _isMatched.every((m) => m(test, a))); if (matched.length === 1) { - const aIndex = remainingAssertions.indexOf(matched[0]) + const aIndex = remainingAssertions.indexOf(matched[0]); if (aIndex < 0) { - throw new Error(`can't find assertion in the list`) + throw new Error(`can't find assertion in the list`); } - results.push(toMatchResult(test, matched[0])) + results.push(toMatchResult(test, matched[0])); if (_trackRemaining) { - remainingAssertions.splice(aIndex, 1) + remainingAssertions.splice(aIndex, 1); } - return + return; } - let err: string + let err: string; if (_onMatchError) { - err = _onMatchError(test, matched) + err = _onMatchError(test, matched); } // if there is an error string, create a test result with it if (err) { - results.push(toMatchResult(test, undefined, err)) - return + results.push(toMatchResult(test, undefined, err)); + return; } if (_trackRemaining) { - remainingTests.push(test) + remainingTests.push(test); } - }) - return [results, remainingTests, remainingAssertions] - } + }); + return [results, remainingTests, remainingAssertions]; + }; if (this.resultsByFilePath[filePath]) { - return this.resultsByFilePath[filePath] + return this.resultsByFilePath[filePath]; } const matchPos = (t: ItBlock, a: TestAssertionStatus): boolean => (a.line !== undefined && a.line >= t.start.line && a.line <= t.end.line) || - (a.location && a.location.line >= t.start.line && a.location.line <= t.end.line) + (a.location && a.location.line >= t.start.line && a.location.line <= t.end.line); - const matchName = (t: ItBlock, a: TestAssertionStatus): boolean => t.name === a.title - const templateLiteralPattern = /\${.*?}/ // template literal pattern + const matchName = (t: ItBlock, a: TestAssertionStatus): boolean => t.name === a.title; + const templateLiteralPattern = /\${.*?}/; // template literal pattern const matchTemplateLiteral = (t: ItBlock, a: TestAssertionStatus): boolean => { if (!t.name.match(templateLiteralPattern)) { - return false + return false; } - const parts = t.name.split(templateLiteralPattern) - const r = parts.every((p) => a.title.includes(p)) - return r - } + const parts = t.name.split(templateLiteralPattern); + const r = parts.every((p) => a.title.includes(p)); + return r; + }; const onMatchError: OnMatchError = (t: ItBlock, match: TestAssertionStatus[]) => { - let err: string + let err: string; if (match.length <= 0 && t.name.match(templateLiteralPattern)) { - err = 'no test result found, could be caused by template literals?' + err = 'no test result found, could be caused by template literals?'; } if (match.length > 1) { - err = 'found multiple potential matches, could be caused by duplicate test names or template literals?' + err = + 'found multiple potential matches, could be caused by duplicate test names or template literals?'; } if (err && this.verbose) { // tslint:disable-next-line: no-console - console.log(`'${t.name}' failed to find test result: ${err}`) + console.log(`'${t.name}' failed to find test result: ${err}`); } - return err - } + return err; + }; - let { itBlocks } = parseTest(filePath) - let assertions = this.reconciler.assertionsForTestFile(filePath) || [] - const totalResult: TestResult[] = [] + let { itBlocks } = parseTest(filePath); + let assertions = this.reconciler.assertionsForTestFile(filePath) || []; + const totalResult: TestResult[] = []; if (assertions.length > 0 && itBlocks.length > 0) { const algorithms: Array<[IsMatched[], OnMatchError]> = [ @@ -151,27 +155,27 @@ export class TestResultProvider { [[matchTemplateLiteral, matchPos], undefined], [[matchTemplateLiteral], undefined], [[matchName], onMatchError], - ] + ]; for (const [matchers, onError] of algorithms) { - let result: TestResult[] - ;[result, itBlocks, assertions] = matchTests(itBlocks, assertions, matchers, onError) - totalResult.push(...result) + let result: TestResult[]; + [result, itBlocks, assertions] = matchTests(itBlocks, assertions, matchers, onError); + totalResult.push(...result); if (itBlocks.length <= 0 || assertions.length <= 0) { - break + break; } } } // convert remaining itBlocks to unmatched result - itBlocks.forEach((t) => totalResult.push(toMatchResult(t))) + itBlocks.forEach((t) => totalResult.push(toMatchResult(t))); - this.resultsByFilePath[filePath] = totalResult - return totalResult + this.resultsByFilePath[filePath] = totalResult; + return totalResult; } getSortedResults(filePath: string) { if (this.sortedResultsByFilePath[filePath]) { - return this.sortedResultsByFilePath[filePath] + return this.sortedResultsByFilePath[filePath]; } const result: SortedTestResults = { @@ -179,32 +183,32 @@ export class TestResultProvider { skip: [], success: [], unknown: [], - } + }; - const testResults = this.getResults(filePath) + const testResults = this.getResults(filePath); for (const test of testResults) { if (test.status === TestReconciliationState.KnownFail) { - result.fail.push(test) + result.fail.push(test); } else if (test.status === TestReconciliationState.KnownSkip) { - result.skip.push(test) + result.skip.push(test); } else if (test.status === TestReconciliationState.KnownSuccess) { - result.success.push(test) + result.success.push(test); } else { - result.unknown.push(test) + result.unknown.push(test); } } - this.sortedResultsByFilePath[filePath] = result - return result + this.sortedResultsByFilePath[filePath] = result; + return result; } updateTestResults(data: JestTotalResults): TestFileAssertionStatus[] { - this.resetCache() - return this.reconciler.updateFileWithJestStatus(data) + this.resetCache(); + return this.reconciler.updateFileWithJestStatus(data); } removeCachedResults(filePath: string) { - this.resultsByFilePath[filePath] = null - this.sortedResultsByFilePath[filePath] = null + this.resultsByFilePath[filePath] = null; + this.sortedResultsByFilePath[filePath] = null; } } diff --git a/src/TestResults/index.ts b/src/TestResults/index.ts index 685de54a7..2f1331cb8 100644 --- a/src/TestResults/index.ts +++ b/src/TestResults/index.ts @@ -1,3 +1,3 @@ -export * from './TestReconciliationState' -export { TestResult, resultsWithLowerCaseWindowsDriveLetters } from './TestResult' -export * from './TestResultProvider' +export * from './TestReconciliationState'; +export { TestResult, resultsWithLowerCaseWindowsDriveLetters } from './TestResult'; +export * from './TestResultProvider'; diff --git a/src/appGlobals.ts b/src/appGlobals.ts index 72fd3eff0..7eee1e8f9 100644 --- a/src/appGlobals.ts +++ b/src/appGlobals.ts @@ -1,2 +1,2 @@ -export const extensionName = 'io.orta.jest' -export const extensionId = 'orta.vscode-jest' +export const extensionName = 'io.orta.jest'; +export const extensionId = 'orta.vscode-jest'; diff --git a/src/decorations.ts b/src/decorations.ts index f5d7a5770..2c445c0b2 100644 --- a/src/decorations.ts +++ b/src/decorations.ts @@ -1,4 +1,4 @@ -import { window, OverviewRulerLane, DecorationRangeBehavior } from 'vscode' +import { window, OverviewRulerLane, DecorationRangeBehavior } from 'vscode'; export function failingItName() { return window.createTextEditorDecorationType({ @@ -17,7 +17,7 @@ export function failingItName() { }, }, rangeBehavior: DecorationRangeBehavior.ClosedClosed, - }) + }); } export function skipItName() { @@ -37,7 +37,7 @@ export function skipItName() { }, }, rangeBehavior: DecorationRangeBehavior.ClosedClosed, - }) + }); } export function passingItName() { @@ -57,7 +57,7 @@ export function passingItName() { }, }, rangeBehavior: DecorationRangeBehavior.ClosedClosed, - }) + }); } export function notRanItName() { @@ -77,7 +77,7 @@ export function notRanItName() { }, }, rangeBehavior: DecorationRangeBehavior.ClosedClosed, - }) + }); } export function failingAssertionStyle(text: string) { @@ -103,5 +103,5 @@ export function failingAssertionStyle(text: string) { contentText: ' // ' + text, }, }, - }) + }); } diff --git a/src/diagnostics.ts b/src/diagnostics.ts index 9f744b87a..7d38220c6 100644 --- a/src/diagnostics.ts +++ b/src/diagnostics.ts @@ -2,16 +2,16 @@ * this module contains functions to show jest test results in * vscode inspector via the DiagnosticsCollection. */ -import * as vscode from 'vscode' -import { existsSync } from 'fs' +import * as vscode from 'vscode'; +import { existsSync } from 'fs'; // import { DiagnosticCollection, Uri, Diagnostic, Range, DiagnosticSeverity } from 'vscode' -import { TestFileAssertionStatus } from 'jest-editor-support' -import { TestReconciliationState, TestResult } from './TestResults' +import { TestFileAssertionStatus } from 'jest-editor-support'; +import { TestReconciliationState, TestResult } from './TestResults'; function createDiagnosticWithRange(message: string, range: vscode.Range): vscode.Diagnostic { - const diag = new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Error) - diag.source = 'Jest' - return diag + const diag = new vscode.Diagnostic(range, message, vscode.DiagnosticSeverity.Error); + diag.source = 'Jest'; + return diag; } function createDiagnostic( @@ -20,8 +20,8 @@ function createDiagnostic( startCol = 0, endCol = Number.MAX_SAFE_INTEGER ): vscode.Diagnostic { - const line = lineNumber > 0 ? lineNumber - 1 : 0 - return createDiagnosticWithRange(message, new vscode.Range(line, startCol, line, endCol)) + const line = lineNumber > 0 ? lineNumber - 1 : 0; + return createDiagnosticWithRange(message, new vscode.Range(line, startCol, line, endCol)); } // update diagnostics for the active editor @@ -31,21 +31,21 @@ export function updateCurrentDiagnostics( diagnostics: vscode.DiagnosticCollection, editor: vscode.TextEditor ) { - const uri = editor.document.uri + const uri = editor.document.uri; if (!testResult.length) { - diagnostics.delete(uri) - return + diagnostics.delete(uri); + return; } diagnostics.set( uri, testResult.map((r) => { - const line = r.lineNumberOfError || r.end.line - const textLine = editor.document.lineAt(line) - return createDiagnosticWithRange(r.shortMessage, textLine.range) + const line = r.lineNumberOfError || r.end.line; + const textLine = editor.document.lineAt(line); + return createDiagnosticWithRange(r.shortMessage, textLine.range); }) - ) + ); } // update all diagnosis with jest test results @@ -54,53 +54,58 @@ export function updateCurrentDiagnostics( // will not have the actual info about text position. However when the file // become active, it will then utilize the actual file content via updateCurrentDiagnostics() -export function updateDiagnostics(testResults: TestFileAssertionStatus[], diagnostics: vscode.DiagnosticCollection) { +export function updateDiagnostics( + testResults: TestFileAssertionStatus[], + diagnostics: vscode.DiagnosticCollection +) { function addTestFileError(result: TestFileAssertionStatus, uri: vscode.Uri) { - const diag = createDiagnostic(result.message || 'test file error', 0, 0, 0) - diagnostics.set(uri, [diag]) + const diag = createDiagnostic(result.message || 'test file error', 0, 0, 0); + diagnostics.set(uri, [diag]); } function addTestsError(result: TestFileAssertionStatus, uri: vscode.Uri) { - const asserts = result.assertions.filter((a) => a.status === TestReconciliationState.KnownFail) + const asserts = result.assertions.filter((a) => a.status === TestReconciliationState.KnownFail); diagnostics.set( uri, - asserts.map((assertion) => createDiagnostic(assertion.shortMessage || assertion.message, assertion.line)) - ) + asserts.map((assertion) => + createDiagnostic(assertion.shortMessage || assertion.message, assertion.line) + ) + ); } testResults.forEach((result) => { - const uri = vscode.Uri.file(result.file) + const uri = vscode.Uri.file(result.file); switch (result.status) { case TestReconciliationState.KnownFail: if (result.assertions.length <= 0) { - addTestFileError(result, uri) + addTestFileError(result, uri); } else { - addTestsError(result, uri) + addTestsError(result, uri); } - break + break; default: - diagnostics.delete(uri) - break + diagnostics.delete(uri); + break; } - }) + }); // Remove diagnostics for files no longer in existence - const toBeDeleted = [] + const toBeDeleted = []; diagnostics.forEach((uri) => { if (!existsSync(uri.fsPath)) { - toBeDeleted.push(uri) + toBeDeleted.push(uri); } - }) + }); toBeDeleted.forEach((uri) => { - diagnostics.delete(uri) - }) + diagnostics.delete(uri); + }); } export function resetDiagnostics(diagnostics: vscode.DiagnosticCollection) { - diagnostics.clear() + diagnostics.clear(); } export function failedSuiteCount(diagnostics: vscode.DiagnosticCollection): number { - let sum = 0 - diagnostics.forEach(() => sum++) - return sum + let sum = 0; + diagnostics.forEach(() => sum++); + return sum; } diff --git a/src/editor.ts b/src/editor.ts index f5c0a583b..29ea93988 100644 --- a/src/editor.ts +++ b/src/editor.ts @@ -1,24 +1,24 @@ -import * as vscode from 'vscode' +import * as vscode from 'vscode'; export function hasDocument(editor: vscode.TextEditor) { - return !!editor && !!editor.document + return !!editor && !!editor.document; } export function isOpenInMultipleEditors(document: vscode.TextDocument) { if (!document || !document.fileName) { - return false + return false; } - let count = 0 + let count = 0; for (const editor of vscode.window.visibleTextEditors) { if (editor && editor.document && editor.document.fileName === document.fileName) { - count += 1 + count += 1; } if (count > 1) { - break + break; } } - return count > 1 + return count > 1; } diff --git a/src/extension.ts b/src/extension.ts index a3872af4d..a1d8de62e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,42 +1,48 @@ -import * as vscode from 'vscode' +import * as vscode from 'vscode'; -import { extensionName } from './appGlobals' -import { statusBar } from './StatusBar' -import { CoverageCodeLensProvider } from './Coverage' -import { ExtensionManager, getExtensionWindowSettings } from './extensionManager' -import { registerSnapshotCodeLens, registerSnapshotPreview } from './SnapshotCodeLens' +import { extensionName } from './appGlobals'; +import { statusBar } from './StatusBar'; +import { CoverageCodeLensProvider } from './Coverage'; +import { ExtensionManager, getExtensionWindowSettings } from './extensionManager'; +import { registerSnapshotCodeLens, registerSnapshotPreview } from './SnapshotCodeLens'; -let extensionManager: ExtensionManager +let extensionManager: ExtensionManager; export function activate(context: vscode.ExtensionContext) { - extensionManager = new ExtensionManager(context) + extensionManager = new ExtensionManager(context); const languages = [ { language: 'javascript' }, { language: 'javascriptreact' }, { language: 'typescript' }, { language: 'typescriptreact' }, - ] + ]; context.subscriptions.push( ...statusBar.register((folder: string) => extensionManager.getByName(folder)), extensionManager.registerCommand(`${extensionName}.start`, (extension) => { - vscode.window.showInformationMessage('Started Jest, press escape to hide this message.') - extension.startProcess() + vscode.window.showInformationMessage('Started Jest, press escape to hide this message.'); + extension.startProcess(); }), - extensionManager.registerCommand(`${extensionName}.stop`, (extension) => extension.stopProcess()), - extensionManager.registerCommand(`${extensionName}.restart`, (extension) => extension.restartProcess()), + extensionManager.registerCommand(`${extensionName}.stop`, (extension) => + extension.stopProcess() + ), + extensionManager.registerCommand(`${extensionName}.restart`, (extension) => + extension.restartProcess() + ), // dublicate of "show-output" maybe remove? - extensionManager.registerCommand(`${extensionName}.show-channel`, (extension) => extension.channel.show()), + extensionManager.registerCommand(`${extensionName}.show-channel`, (extension) => + extension.channel.show() + ), extensionManager.registerCommand(`${extensionName}.coverage.toggle`, (extension) => extension.toggleCoverageOverlay() ), vscode.commands.registerCommand( `${extensionName}.run-test`, (document: vscode.TextDocument, filename: string, identifier: string) => { - const workspace = vscode.workspace.getWorkspaceFolder(document.uri) - extensionManager.getByName(workspace.name).runTest(workspace, filename, identifier) + const workspace = vscode.workspace.getWorkspaceFolder(document.uri); + extensionManager.getByName(workspace.name).runTest(workspace, filename, identifier); } ), ...registerSnapshotCodeLens(getExtensionWindowSettings().enableSnapshotPreviews), @@ -47,20 +53,41 @@ export function activate(context: vscode.ExtensionContext) { ), vscode.languages.registerCodeLensProvider(languages, extensionManager.debugCodeLensProvider), // this provides the opportunity to inject test names into the DebugConfiguration - vscode.debug.registerDebugConfigurationProvider('node', extensionManager.debugConfigurationProvider), + vscode.debug.registerDebugConfigurationProvider( + 'node', + extensionManager.debugConfigurationProvider + ), // this provides the snippets generation - vscode.debug.registerDebugConfigurationProvider('vscode-jest-tests', extensionManager.debugConfigurationProvider), - vscode.workspace.onDidChangeConfiguration(extensionManager.onDidChangeConfiguration, extensionManager), + vscode.debug.registerDebugConfigurationProvider( + 'vscode-jest-tests', + extensionManager.debugConfigurationProvider + ), + vscode.workspace.onDidChangeConfiguration( + extensionManager.onDidChangeConfiguration, + extensionManager + ), - vscode.workspace.onDidChangeWorkspaceFolders(extensionManager.onDidChangeWorkspaceFolders, extensionManager), + vscode.workspace.onDidChangeWorkspaceFolders( + extensionManager.onDidChangeWorkspaceFolders, + extensionManager + ), - vscode.workspace.onDidCloseTextDocument(extensionManager.onDidCloseTextDocument, extensionManager), + vscode.workspace.onDidCloseTextDocument( + extensionManager.onDidCloseTextDocument, + extensionManager + ), - vscode.window.onDidChangeActiveTextEditor(extensionManager.onDidChangeActiveTextEditor, extensionManager), - vscode.workspace.onDidChangeTextDocument(extensionManager.onDidChangeTextDocument, extensionManager) - ) + vscode.window.onDidChangeActiveTextEditor( + extensionManager.onDidChangeActiveTextEditor, + extensionManager + ), + vscode.workspace.onDidChangeTextDocument( + extensionManager.onDidChangeTextDocument, + extensionManager + ) + ); } export function deactivate() { - extensionManager.unregisterAll() + extensionManager.unregisterAll(); } diff --git a/src/extensionManager.ts b/src/extensionManager.ts index 0590fd5bd..747549eab 100644 --- a/src/extensionManager.ts +++ b/src/extensionManager.ts @@ -1,17 +1,17 @@ -import * as vscode from 'vscode' -import * as path from 'path' -import { ProjectWorkspace } from 'jest-editor-support' -import { pathToJest, pathToConfig } from './helpers' -import { JestExt } from './JestExt' -import { DebugCodeLensProvider, TestState } from './DebugCodeLens' -import { DebugConfigurationProvider } from './DebugConfigurationProvider' -import { PluginResourceSettings, PluginWindowSettings } from './Settings' -import { statusBar } from './StatusBar' +import * as vscode from 'vscode'; +import * as path from 'path'; +import { ProjectWorkspace } from 'jest-editor-support'; +import { pathToJest, pathToConfig } from './helpers'; +import { JestExt } from './JestExt'; +import { DebugCodeLensProvider, TestState } from './DebugCodeLens'; +import { DebugConfigurationProvider } from './DebugConfigurationProvider'; +import { PluginResourceSettings, PluginWindowSettings } from './Settings'; +import { statusBar } from './StatusBar'; -export type GetJestExtByURI = (uri: vscode.Uri) => JestExt | undefined +export type GetJestExtByURI = (uri: vscode.Uri) => JestExt | undefined; export function getExtensionWindowSettings(): PluginWindowSettings { - const config = vscode.workspace.getConfiguration('jest') + const config = vscode.workspace.getConfiguration('jest'); return { debugCodeLens: { enabled: config.get<boolean>('enableCodeLens'), @@ -19,11 +19,11 @@ export function getExtensionWindowSettings(): PluginWindowSettings { }, enableSnapshotPreviews: config.get<boolean>('enableSnapshotPreviews'), disabledWorkspaceFolders: config.get<string[]>('disabledWorkspaceFolders'), - } + }; } export function getExtensionResourceSettings(uri: vscode.Uri): PluginResourceSettings { - const config = vscode.workspace.getConfiguration('jest', uri) + const config = vscode.workspace.getConfiguration('jest', uri); return { autoEnable: config.get<boolean>('autoEnable'), enableInlineErrorMessages: config.get<boolean>('enableInlineErrorMessages'), @@ -36,45 +36,47 @@ export function getExtensionResourceSettings(uri: vscode.Uri): PluginResourceSet showCoverageOnLoad: config.get<boolean>('showCoverageOnLoad'), coverageFormatter: config.get<string>('coverageFormatter'), debugMode: config.get<boolean>('debugMode'), - } + }; } export class ExtensionManager { - debugCodeLensProvider: DebugCodeLensProvider - debugConfigurationProvider: DebugConfigurationProvider + debugCodeLensProvider: DebugCodeLensProvider; + debugConfigurationProvider: DebugConfigurationProvider; - private extByWorkspace: Map<string, JestExt> = new Map() - private context: vscode.ExtensionContext - private commonPluginSettings: PluginWindowSettings + private extByWorkspace: Map<string, JestExt> = new Map(); + private context: vscode.ExtensionContext; + private commonPluginSettings: PluginWindowSettings; constructor(context: vscode.ExtensionContext) { - this.context = context + this.context = context; - this.commonPluginSettings = getExtensionWindowSettings() + this.commonPluginSettings = getExtensionWindowSettings(); - this.debugConfigurationProvider = new DebugConfigurationProvider() - this.debugCodeLensProvider = new DebugCodeLensProvider((uri) => this.getByDocUri(uri)) - this.applySettings(getExtensionWindowSettings()) - this.registerAll() + this.debugConfigurationProvider = new DebugConfigurationProvider(); + this.debugCodeLensProvider = new DebugCodeLensProvider((uri) => this.getByDocUri(uri)); + this.applySettings(getExtensionWindowSettings()); + this.registerAll(); } applySettings(settings: PluginWindowSettings) { - this.commonPluginSettings = settings - const { debugCodeLens } = settings - this.debugCodeLensProvider.showWhenTestStateIn = debugCodeLens.enabled ? debugCodeLens.showWhenTestStateIn : [] - settings.disabledWorkspaceFolders.forEach(this.unregisterByName, this) + this.commonPluginSettings = settings; + const { debugCodeLens } = settings; + this.debugCodeLensProvider.showWhenTestStateIn = debugCodeLens.enabled + ? debugCodeLens.showWhenTestStateIn + : []; + settings.disabledWorkspaceFolders.forEach(this.unregisterByName, this); } register(workspaceFolder: vscode.WorkspaceFolder) { if (!this.shouldStart(workspaceFolder.name)) { - return + return; } - const pluginSettings = getExtensionResourceSettings(workspaceFolder.uri) - const jestPath = pathToJest(pluginSettings) - const configPath = pathToConfig(pluginSettings) - const currentJestVersion = 20 - const debugMode = pluginSettings.debugMode + const pluginSettings = getExtensionResourceSettings(workspaceFolder.uri); + const jestPath = pathToJest(pluginSettings); + const configPath = pathToConfig(pluginSettings); + const currentJestVersion = 20; + const debugMode = pluginSettings.debugMode; const instanceSettings = { multirootEnv: vscode.workspace.workspaceFolders.length > 1, - } + }; const jestWorkspace = new ProjectWorkspace( pluginSettings.rootPath, jestPath, @@ -83,12 +85,14 @@ export class ExtensionManager { workspaceFolder.name, null, debugMode - ) + ); // Create our own console - const channel = vscode.window.createOutputChannel(`Jest (${workspaceFolder.name})`) + const channel = vscode.window.createOutputChannel(`Jest (${workspaceFolder.name})`); - const failDiagnostics = vscode.languages.createDiagnosticCollection(`Jest (${workspaceFolder.name})`) + const failDiagnostics = vscode.languages.createDiagnosticCollection( + `Jest (${workspaceFolder.name})` + ); this.extByWorkspace.set( workspaceFolder.name, @@ -103,105 +107,109 @@ export class ExtensionManager { failDiagnostics, instanceSettings ) - ) + ); } registerAll() { - vscode.workspace.workspaceFolders.forEach(this.register, this) + vscode.workspace.workspaceFolders.forEach(this.register, this); } unregister(workspaceFolder: vscode.WorkspaceFolder) { - this.unregisterByName(workspaceFolder.name) + this.unregisterByName(workspaceFolder.name); } unregisterByName(name: string) { - const extension = this.extByWorkspace.get(name) + const extension = this.extByWorkspace.get(name); if (extension) { - extension.deactivate() - this.extByWorkspace.delete(name) + extension.deactivate(); + this.extByWorkspace.delete(name); } } unregisterAll() { - const keys = this.extByWorkspace.keys() + const keys = this.extByWorkspace.keys(); for (const key of keys) { - this.unregisterByName(key) + this.unregisterByName(key); } } shouldStart(workspaceFolderName: string): boolean { const { commonPluginSettings: { disabledWorkspaceFolders }, - } = this + } = this; if (this.extByWorkspace.has(workspaceFolderName)) { - return false + return false; } if (disabledWorkspaceFolders.includes(workspaceFolderName)) { - return false + return false; } - return true + return true; } getByName(workspaceFolderName: string) { - return this.extByWorkspace.get(workspaceFolderName) + return this.extByWorkspace.get(workspaceFolderName); } public getByDocUri: GetJestExtByURI = (uri: vscode.Uri) => { - const workspace = vscode.workspace.getWorkspaceFolder(uri) + const workspace = vscode.workspace.getWorkspaceFolder(uri); if (workspace) { - return this.getByName(workspace.name) + return this.getByName(workspace.name); } - } + }; async get() { const workspace = vscode.workspace.workspaceFolders.length <= 1 ? vscode.workspace.workspaceFolders[0] - : await vscode.window.showWorkspaceFolderPick() + : await vscode.window.showWorkspaceFolderPick(); - const instance = workspace && this.getByName(workspace.name) + const instance = workspace && this.getByName(workspace.name); if (instance) { - return instance + return instance; } else if (workspace) { - throw new Error(`No Jest instance in ${workspace.name} workspace`) + throw new Error(`No Jest instance in ${workspace.name} workspace`); } } - registerCommand(command: string, callback: (extension: JestExt, ...args: any[]) => any, thisArg?: any) { + registerCommand( + command: string, + callback: (extension: JestExt, ...args: any[]) => any, + thisArg?: any + ) { return vscode.commands.registerCommand(command, async (...args) => { - const extension = await this.get() + const extension = await this.get(); if (extension) { - callback.call(thisArg, extension, ...args) + callback.call(thisArg, extension, ...args); } - }) + }); } onDidChangeConfiguration(e: vscode.ConfigurationChangeEvent) { if (e.affectsConfiguration('jest')) { - this.applySettings(getExtensionWindowSettings()) - this.registerAll() + this.applySettings(getExtensionWindowSettings()); + this.registerAll(); } vscode.workspace.workspaceFolders.forEach((workspaceFolder) => { - const jestExt = this.getByName(workspaceFolder.name) + const jestExt = this.getByName(workspaceFolder.name); if (jestExt && e.affectsConfiguration('jest', workspaceFolder.uri)) { - const updatedSettings = getExtensionResourceSettings(workspaceFolder.uri) - jestExt.triggerUpdateSettings(updatedSettings) + const updatedSettings = getExtensionResourceSettings(workspaceFolder.uri); + jestExt.triggerUpdateSettings(updatedSettings); } - }) + }); } onDidChangeWorkspaceFolders(e: vscode.WorkspaceFoldersChangeEvent) { - e.added.forEach(this.register, this) - e.removed.forEach(this.unregister, this) + e.added.forEach(this.register, this); + e.removed.forEach(this.unregister, this); } onDidCloseTextDocument(document: vscode.TextDocument) { - const ext = this.getByDocUri(document.uri) + const ext = this.getByDocUri(document.uri); if (ext) { - ext.onDidCloseTextDocument(document) + ext.onDidCloseTextDocument(document); } } onDidChangeActiveTextEditor(editor: vscode.TextEditor) { if (editor && editor.document) { - statusBar.onDidChangeActiveTextEditor(editor) - const ext = this.getByDocUri(editor.document.uri) + statusBar.onDidChangeActiveTextEditor(editor); + const ext = this.getByDocUri(editor.document.uri); if (ext) { - ext.onDidChangeActiveTextEditor(editor) + ext.onDidChangeActiveTextEditor(editor); } } } onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) { - const ext = this.getByDocUri(event.document.uri) + const ext = this.getByDocUri(event.document.uri); if (ext) { - ext.onDidChangeTextDocument(event) + ext.onDidChangeTextDocument(event); } } } diff --git a/src/helpers.ts b/src/helpers.ts index 0860cb37e..c6684ec08 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -1,18 +1,23 @@ -import { platform } from 'os' -import { existsSync, readFileSync } from 'fs' -import { normalize, join } from 'path' +import { platform } from 'os'; +import { existsSync, readFileSync } from 'fs'; +import { normalize, join } from 'path'; -import { PluginResourceSettings, hasUserSetPathToJest } from './Settings' +import { PluginResourceSettings, hasUserSetPathToJest } from './Settings'; /** * Known binary names of `react-scripts` forks */ -const createReactAppBinaryNames = ['react-scripts', 'react-native-scripts', 'react-scripts-ts', 'react-app-rewired'] +const createReactAppBinaryNames = [ + 'react-scripts', + 'react-native-scripts', + 'react-scripts-ts', + 'react-app-rewired', +]; /** * File extension for npm binaries */ -export const nodeBinExtension: string = platform() === 'win32' ? '.cmd' : '' +export const nodeBinExtension: string = platform() === 'win32' ? '.cmd' : ''; /** * Resolves the location of an npm binary @@ -20,8 +25,10 @@ export const nodeBinExtension: string = platform() === 'win32' ? '.cmd' : '' * Returns the path if it exists, or `undefined` otherwise */ function getLocalPathForExecutable(rootPath: string, executable: string): string { - const absolutePath = normalize(join(rootPath, 'node_modules', '.bin', executable + nodeBinExtension)) - return existsSync(absolutePath) ? absolutePath : undefined + const absolutePath = normalize( + join(rootPath, 'node_modules', '.bin', executable + nodeBinExtension) + ); + return existsSync(absolutePath) ? absolutePath : undefined; } /** @@ -33,14 +40,14 @@ function getLocalPathForExecutable(rootPath: string, executable: string): string */ export function getTestCommand(rootPath: string): string | undefined | null { try { - const packagePath = join(rootPath, 'package.json') - const packageJSON = JSON.parse(readFileSync(packagePath, 'utf8')) + const packagePath = join(rootPath, 'package.json'); + const packageJSON = JSON.parse(readFileSync(packagePath, 'utf8')); if (packageJSON && packageJSON.scripts && packageJSON.scripts.test) { - return packageJSON.scripts.test + return packageJSON.scripts.test; } - return null + return null; } catch { - return undefined + return undefined; } } @@ -48,20 +55,25 @@ export function getTestCommand(rootPath: string): string | undefined | null { * Checks if the supplied test command could have been generated by create-react-app */ export function isCreateReactAppTestCommand(testCommand: string): boolean { - return !!testCommand && createReactAppBinaryNames.some((binary) => testCommand.includes(`${binary} test`)) + return ( + !!testCommand && + createReactAppBinaryNames.some((binary) => testCommand.includes(`${binary} test`)) + ); } /** * Checks if the project in `rootPath` was bootstrapped by `create-react-app`. */ function isBootstrappedWithCreateReactApp(rootPath: string): boolean { - const testCommand = getTestCommand(rootPath) + const testCommand = getTestCommand(rootPath); if (testCommand === undefined) { // In case parsing `package.json` failed or was unconclusive, // fallback to checking for the presence of the binaries in `./node_modules/.bin` - return createReactAppBinaryNames.some((binary) => getLocalPathForExecutable(rootPath, binary) !== undefined) + return createReactAppBinaryNames.some( + (binary) => getLocalPathForExecutable(rootPath, binary) !== undefined + ); } - return isCreateReactAppTestCommand(testCommand) + return isCreateReactAppTestCommand(testCommand); } /** @@ -72,15 +84,15 @@ function isBootstrappedWithCreateReactApp(rootPath: string): boolean { // tslint:disable-next-line no-shadowed-variable export function pathToJest({ pathToJest, rootPath }: PluginResourceSettings) { if (hasUserSetPathToJest(pathToJest)) { - return normalize(pathToJest) + return normalize(pathToJest); } if (isBootstrappedWithCreateReactApp(rootPath)) { - return 'npm test --' + return 'npm test --'; } - const p = getLocalPathForExecutable(rootPath, 'jest') || 'jest' + nodeBinExtension - return `"${p}"` + const p = getLocalPathForExecutable(rootPath, 'jest') || 'jest' + nodeBinExtension; + return `"${p}"`; } /** @@ -90,23 +102,26 @@ export function pathToJest({ pathToJest, rootPath }: PluginResourceSettings) { */ export function pathToConfig(pluginSettings: PluginResourceSettings) { if (pluginSettings.pathToConfig !== '') { - return normalize(pluginSettings.pathToConfig) + return normalize(pluginSettings.pathToConfig); } - return '' + return ''; } /** * Taken From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions */ export function escapeRegExp(str: string): string { - return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') // $& means the whole matched string + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } /** * ANSI colors/characters cleaning based on http://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings */ export function cleanAnsi(str: string): string { - // eslint-disable-next-line no-control-regex - return str.replace(/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, '') + return str.replace( + // eslint-disable-next-line no-control-regex + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, + '' + ); } diff --git a/src/messaging.ts b/src/messaging.ts index 53f154f8c..5fc8a4f36 100644 --- a/src/messaging.ts +++ b/src/messaging.ts @@ -2,46 +2,54 @@ * collection of functions to show messages with actions in a consistent manner */ -import * as vscode from 'vscode' +import * as vscode from 'vscode'; -export const TROUBLESHOOTING_URL = 'https://github.com/jest-community/vscode-jest/blob/master/README.md#troubleshooting' +export const TROUBLESHOOTING_URL = + 'https://github.com/jest-community/vscode-jest/blob/master/README.md#troubleshooting'; // // internal methods // function _extractActionTitles(actions?: MessageAction[]): string[] { - return actions ? actions.map((a) => a.title) : [] + return actions ? actions.map((a) => a.title) : []; } // expose the internal function so we can unit testing it export function _handleMessageActions(actions?: MessageAction[]): (action?: string) => void { return (action?: string) => { if (!action) { - return + return; } - const found = actions.filter((a) => a.title === action) + const found = actions.filter((a) => a.title === action); if (found.length === 1) { - found[0].action() + found[0].action(); } else { - throw Error(`expect exactly one matched action '${action}' but found ${found.length} match(es)`) + throw Error( + `expect exactly one matched action '${action}' but found ${found.length} match(es)` + ); } - } + }; } export interface MessageAction { - title: string - action: () => void + title: string; + action: () => void; } export function systemErrorMessage(message: string, ...actions: MessageAction[]) { - vscode.window.showErrorMessage(message, ..._extractActionTitles(actions)).then(_handleMessageActions(actions)) + vscode.window + .showErrorMessage(message, ..._extractActionTitles(actions)) + .then(_handleMessageActions(actions)); } export function systemWarningMessage(message: string, ...actions: MessageAction[]) { - vscode.window.showWarningMessage(message, ..._extractActionTitles(actions)).then(_handleMessageActions(actions)) + vscode.window + .showWarningMessage(message, ..._extractActionTitles(actions)) + .then(_handleMessageActions(actions)); } // common actions export const showTroubleshootingAction: MessageAction = { title: 'Help', - action: () => vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(TROUBLESHOOTING_URL)), -} + action: () => + vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(TROUBLESHOOTING_URL)), +}; diff --git a/src/reporter.ts b/src/reporter.ts index 8fbdd96aa..816b77b31 100644 --- a/src/reporter.ts +++ b/src/reporter.ts @@ -2,12 +2,12 @@ class VSCodeJestReporter { onRunStart() { // tslint:disable-next-line: no-console - console.log('onRunStart') + console.log('onRunStart'); } onRunComplete() { // tslint:disable-next-line: no-console - console.log('onRunComplete') + console.log('onRunComplete'); } } -module.exports = VSCodeJestReporter +module.exports = VSCodeJestReporter; diff --git a/src/types.ts b/src/types.ts index 83c2f61aa..bb7d94f1c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,5 @@ -import * as vscode from 'vscode' +import * as vscode from 'vscode'; export interface DecorationOptions extends vscode.DecorationOptions { - identifier: string + identifier: string; } diff --git a/tests/Coverage/CoverageCodeLensProvider.test.ts b/tests/Coverage/CoverageCodeLensProvider.test.ts index 5b6974c0f..3308e6ca7 100644 --- a/tests/Coverage/CoverageCodeLensProvider.test.ts +++ b/tests/Coverage/CoverageCodeLensProvider.test.ts @@ -1,15 +1,15 @@ -jest.unmock('../../src/Coverage/CoverageCodeLensProvider') +jest.unmock('../../src/Coverage/CoverageCodeLensProvider'); // tslint:disable max-classes-per-file -const rangeConstructor = jest.fn() +const rangeConstructor = jest.fn(); jest.mock('vscode', () => { class CodeLens { - range: any - command: any + range: any; + command: any; constructor(range, command) { - this.range = range - this.command = command + this.range = range; + this.command = command; } } @@ -18,23 +18,23 @@ jest.mock('vscode', () => { // } class Position { - lineNumber: string - character: string + lineNumber: string; + character: string; constructor(lineNumber, character) { - this.lineNumber = lineNumber - this.character = character + this.lineNumber = lineNumber; + this.character = character; } } class Range { - start: Position - end: Position + start: Position; + end: Position; constructor(start, end) { - rangeConstructor() - this.start = start - this.end = end + rangeConstructor(); + this.start = start; + this.end = end; } } @@ -42,32 +42,32 @@ jest.mock('vscode', () => { CodeLens, Position, Range, - } -}) + }; +}); -import { CoverageCodeLensProvider } from '../../src/Coverage/CoverageCodeLensProvider' +import { CoverageCodeLensProvider } from '../../src/Coverage/CoverageCodeLensProvider'; // import * as vscode from 'vscode' describe('CoverageCodeLensProvider', () => { - let mockJestExt - let provider + let mockJestExt; + let provider; beforeEach(() => { mockJestExt = { coverageMapProvider: { getFileCoverage: jest.fn() }, - } - const mockGetExt = jest.fn().mockReturnValue(mockJestExt) - provider = new CoverageCodeLensProvider(mockGetExt) - }) + }; + const mockGetExt = jest.fn().mockReturnValue(mockJestExt); + provider = new CoverageCodeLensProvider(mockGetExt); + }); describe('provideCodeLenses', () => { - const doc = { fileName: 'file.js' } + const doc = { fileName: 'file.js' }; test('do nothing when no coverage', () => { - mockJestExt.coverageMapProvider.getFileCoverage = () => null - const result = provider.provideCodeLenses(doc) - expect(result).toBeUndefined() - }) + mockJestExt.coverageMapProvider.getFileCoverage = () => null; + const result = provider.provideCodeLenses(doc); + expect(result).toBeUndefined(); + }); test('can summarize', () => { const coverage = { @@ -77,11 +77,11 @@ describe('CoverageCodeLensProvider', () => { lines: { pct: 46.15 }, }), }), - } - mockJestExt.coverageMapProvider.getFileCoverage = () => coverage - const result = provider.provideCodeLenses(doc) - expect(result).toHaveLength(1) - expect(result[0].command.title).toEqual('branches: 10%, lines: 46.15%') - }) - }) -}) + }; + mockJestExt.coverageMapProvider.getFileCoverage = () => coverage; + const result = provider.provideCodeLenses(doc); + expect(result).toHaveLength(1); + expect(result[0].command.title).toEqual('branches: 10%, lines: 46.15%'); + }); + }); +}); diff --git a/tests/Coverage/CoverageMapProvider.test.ts b/tests/Coverage/CoverageMapProvider.test.ts index 1829c1b17..e488cc58b 100644 --- a/tests/Coverage/CoverageMapProvider.test.ts +++ b/tests/Coverage/CoverageMapProvider.test.ts @@ -1,93 +1,93 @@ -jest.unmock('../../src/Coverage/CoverageMapProvider') +jest.unmock('../../src/Coverage/CoverageMapProvider'); -import { CoverageMapProvider } from '../../src/Coverage/CoverageMapProvider' -import { createCoverageMap } from 'istanbul-lib-coverage' -import { createSourceMapStore } from 'istanbul-lib-source-maps' +import { CoverageMapProvider } from '../../src/Coverage/CoverageMapProvider'; +import { createCoverageMap } from 'istanbul-lib-coverage'; +import { createSourceMapStore } from 'istanbul-lib-source-maps'; describe('CoverageMapProvider', () => { describe('constructor()', () => { it('should initialize the coverage map', () => { - const expected: any = {} - ;(createCoverageMap as jest.Mock<any>).mockReturnValueOnce(expected) - const sut = new CoverageMapProvider() + const expected: any = {}; + (createCoverageMap as jest.Mock<any>).mockReturnValueOnce(expected); + const sut = new CoverageMapProvider(); - expect(sut.map).toBe(expected) - }) - }) + expect(sut.map).toBe(expected); + }); + }); describe('map', () => { it('should return the coverage map', () => { - ;(createCoverageMap as jest.Mock<any>).mockImplementation((map) => map) + (createCoverageMap as jest.Mock<any>).mockImplementation((map) => map); createSourceMapStore.mockReturnValueOnce({ transformCoverage: (map) => ({ map }), - }) + }); - const expected: any = {} - const sut = new CoverageMapProvider() - sut.update(expected) + const expected: any = {}; + const sut = new CoverageMapProvider(); + sut.update(expected); - expect(sut.map).toBe(expected) - }) + expect(sut.map).toBe(expected); + }); it('should be a read-only property', () => { - const sut = new CoverageMapProvider() + const sut = new CoverageMapProvider(); // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore: Writing to readonly property - expect(() => (sut.map = {} as any)).toThrow(TypeError) - }) - }) + expect(() => (sut.map = {} as any)).toThrow(TypeError); + }); + }); describe('update()', () => { it('should transform the coverage map', () => { - const expected: any = {} - ;(createCoverageMap as jest.Mock<any>).mockImplementation((map) => map) - const transformCoverage = jest.fn().mockImplementationOnce((map) => ({ map })) - createSourceMapStore.mockReturnValueOnce({ transformCoverage }) + const expected: any = {}; + (createCoverageMap as jest.Mock<any>).mockImplementation((map) => map); + const transformCoverage = jest.fn().mockImplementationOnce((map) => ({ map })); + createSourceMapStore.mockReturnValueOnce({ transformCoverage }); - const sut = new CoverageMapProvider() - sut.update(expected) + const sut = new CoverageMapProvider(); + sut.update(expected); - expect(transformCoverage).toBeCalledWith(expected) - }) + expect(transformCoverage).toBeCalledWith(expected); + }); it('should store the transformed coverage map', () => { - const expected: any = {} + const expected: any = {}; createSourceMapStore.mockReturnValueOnce({ transformCoverage: () => ({ map: expected }), - }) + }); - const sut = new CoverageMapProvider() - sut.update(expected) + const sut = new CoverageMapProvider(); + sut.update(expected); - expect(sut.map).toBe(expected) - }) + expect(sut.map).toBe(expected); + }); it('can preserve the previous maps', () => { - const map1: any = {} - const map2: any = {} + const map1: any = {}; + const map2: any = {}; - const mergeFn = jest.fn() - ;(createCoverageMap as jest.Mock<any>).mockReturnValueOnce({ + const mergeFn = jest.fn(); + (createCoverageMap as jest.Mock<any>).mockReturnValueOnce({ data: {}, merge: mergeFn, - }) + }); createSourceMapStore.mockReturnValue({ transformCoverage: (m) => ({ map: m }), - }) + }); - const sut = new CoverageMapProvider() - sut.update(map1) - sut.update(map2) + const sut = new CoverageMapProvider(); + sut.update(map1); + sut.update(map2); - expect(mergeFn).toBeCalledTimes(2) - }) - }) + expect(mergeFn).toBeCalledTimes(2); + }); + }); describe('getFileCoverage()', () => { it('should return the file coverage if found', () => { - const filePath = 'file.js' - const expected: any = {} + const filePath = 'file.js'; + const expected: any = {}; createSourceMapStore.mockReturnValueOnce({ transformCoverage: () => ({ @@ -97,21 +97,21 @@ describe('CoverageMapProvider', () => { }, }, }), - }) + }); - const sut = new CoverageMapProvider() - sut.update(undefined) + const sut = new CoverageMapProvider(); + sut.update(undefined); - expect(sut.getFileCoverage(filePath)).toBe(expected) - }) + expect(sut.getFileCoverage(filePath)).toBe(expected); + }); it('should return nothing when the file path is not found', () => { - ;(createCoverageMap as jest.Mock<any>).mockReturnValueOnce({ + (createCoverageMap as jest.Mock<any>).mockReturnValueOnce({ data: {}, - }) - const sut = new CoverageMapProvider() + }); + const sut = new CoverageMapProvider(); - expect(sut.getFileCoverage('unknown')).toBeUndefined() - }) - }) -}) + expect(sut.getFileCoverage('unknown')).toBeUndefined(); + }); + }); +}); diff --git a/tests/Coverage/CoverageOverlay.test.ts b/tests/Coverage/CoverageOverlay.test.ts index 22d22f4a3..dce70adbb 100644 --- a/tests/Coverage/CoverageOverlay.test.ts +++ b/tests/Coverage/CoverageOverlay.test.ts @@ -1,159 +1,159 @@ -jest.unmock('../../src/Coverage/CoverageOverlay') +jest.unmock('../../src/Coverage/CoverageOverlay'); const vscodeProperties = { window: { visibleTextEditors: jest.fn(), }, -} +}; jest.mock('vscode', () => { const vscode = { OverviewRulerLane: {}, window: { createTextEditorDecorationType: jest.fn(), }, - } + }; Object.defineProperty(vscode.window, 'visibleTextEditors', { get: () => vscodeProperties.window.visibleTextEditors(), - }) + }); - return vscode -}) + return vscode; +}); -import { CoverageOverlay } from '../../src/Coverage/CoverageOverlay' -import { DefaultFormatter } from '../../src/Coverage/Formatters/DefaultFormatter' -import { hasDocument } from '../../src/editor' +import { CoverageOverlay } from '../../src/Coverage/CoverageOverlay'; +import { DefaultFormatter } from '../../src/Coverage/Formatters/DefaultFormatter'; +import { hasDocument } from '../../src/editor'; describe('CoverageOverlay', () => { - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; describe('constructor', () => { it('should set the default visibility', () => { - const sut = new CoverageOverlay(null, coverageMapProvider) + const sut = new CoverageOverlay(null, coverageMapProvider); - expect(sut.enabled).toBe(CoverageOverlay.defaultVisibility) - }) + expect(sut.enabled).toBe(CoverageOverlay.defaultVisibility); + }); it('should set the visibility if provided', () => { - const enabled = !CoverageOverlay.defaultVisibility - const sut = new CoverageOverlay(null, coverageMapProvider, enabled) + const enabled = !CoverageOverlay.defaultVisibility; + const sut = new CoverageOverlay(null, coverageMapProvider, enabled); - expect(sut.enabled).toBe(enabled) - }) + expect(sut.enabled).toBe(enabled); + }); it('should set the default overlay formatter', () => { - const sut = new CoverageOverlay(null, coverageMapProvider) + const sut = new CoverageOverlay(null, coverageMapProvider); - expect(DefaultFormatter).toBeCalledWith(coverageMapProvider) - expect(sut.formatter).toBeInstanceOf(DefaultFormatter) - }) - }) + expect(DefaultFormatter).toBeCalledWith(coverageMapProvider); + expect(sut.formatter).toBeInstanceOf(DefaultFormatter); + }); + }); describe('enabled', () => { describe('get', () => { it('should return the overlay visibility', () => { - const expected = true - const sut = new CoverageOverlay(null, coverageMapProvider, expected) + const expected = true; + const sut = new CoverageOverlay(null, coverageMapProvider, expected); - expect(sut.enabled).toBe(expected) - }) - }) + expect(sut.enabled).toBe(expected); + }); + }); describe('set', () => { it('should set the overlay visibility', () => { - const expected = true - const sut = new CoverageOverlay(null, coverageMapProvider, !expected) - sut.updateVisibleEditors = jest.fn() - sut.enabled = expected + const expected = true; + const sut = new CoverageOverlay(null, coverageMapProvider, !expected); + sut.updateVisibleEditors = jest.fn(); + sut.enabled = expected; - expect(sut.enabled).toBe(expected) - }) + expect(sut.enabled).toBe(expected); + }); it('should refresh the overlays in visible editors', () => { - const sut = new CoverageOverlay(null, coverageMapProvider) - sut.updateVisibleEditors = jest.fn() - sut.enabled = true + const sut = new CoverageOverlay(null, coverageMapProvider); + sut.updateVisibleEditors = jest.fn(); + sut.enabled = true; - expect(sut.updateVisibleEditors).toBeCalled() - }) - }) - }) + expect(sut.updateVisibleEditors).toBeCalled(); + }); + }); + }); describe('toggleVisibility()', () => { it('should enable the overlay when disabled', () => { - const enabled = false - const sut = new CoverageOverlay(null, coverageMapProvider, enabled) - sut.updateVisibleEditors = jest.fn() - sut.toggleVisibility() + const enabled = false; + const sut = new CoverageOverlay(null, coverageMapProvider, enabled); + sut.updateVisibleEditors = jest.fn(); + sut.toggleVisibility(); - expect(sut.enabled).toBe(true) - }) + expect(sut.enabled).toBe(true); + }); it('should disable the overlay when enabled', () => { - const enabled = true - const sut = new CoverageOverlay(null, coverageMapProvider, enabled) - sut.updateVisibleEditors = jest.fn() - sut.toggleVisibility() + const enabled = true; + const sut = new CoverageOverlay(null, coverageMapProvider, enabled); + sut.updateVisibleEditors = jest.fn(); + sut.toggleVisibility(); - expect(sut.enabled).toBe(false) - }) + expect(sut.enabled).toBe(false); + }); it('should refresh the overlays in visible editors', () => { - const sut = new CoverageOverlay(null, coverageMapProvider) - sut.updateVisibleEditors = jest.fn() - sut.toggleVisibility() + const sut = new CoverageOverlay(null, coverageMapProvider); + sut.updateVisibleEditors = jest.fn(); + sut.toggleVisibility(); - expect(sut.updateVisibleEditors).toBeCalled() - }) - }) + expect(sut.updateVisibleEditors).toBeCalled(); + }); + }); describe('updateVisibleEditors()', () => { it('should update each editor', () => { - const editors = [{}, {}, {}] - vscodeProperties.window.visibleTextEditors.mockReturnValueOnce(editors) + const editors = [{}, {}, {}]; + vscodeProperties.window.visibleTextEditors.mockReturnValueOnce(editors); - const sut = new CoverageOverlay(null, coverageMapProvider) - sut.update = jest.fn() - sut.updateVisibleEditors() + const sut = new CoverageOverlay(null, coverageMapProvider); + sut.update = jest.fn(); + sut.updateVisibleEditors(); for (let i = 0; i < editors.length; i += 1) { - expect((sut.update as jest.Mock<any>).mock.calls[i]).toEqual([editors[i]]) + expect((sut.update as jest.Mock<any>).mock.calls[i]).toEqual([editors[i]]); } - }) - }) + }); + }); describe('update()', () => { it('should do nothing if the editor does not have a valid document', () => { - const sut = new CoverageOverlay(null, coverageMapProvider) - ;((hasDocument as unknown) as jest.Mock<{}>).mockReturnValueOnce(false) + const sut = new CoverageOverlay(null, coverageMapProvider); + ((hasDocument as unknown) as jest.Mock<{}>).mockReturnValueOnce(false); - const editor: any = {} - sut.update(editor) + const editor: any = {}; + sut.update(editor); - expect(sut.formatter.format).not.toBeCalled() - expect(sut.formatter.clear).not.toBeCalled() - }) + expect(sut.formatter.format).not.toBeCalled(); + expect(sut.formatter.clear).not.toBeCalled(); + }); it('should add the overlay when enabled', () => { - const enabled = true - const sut = new CoverageOverlay(null, coverageMapProvider, enabled) - ;((hasDocument as unknown) as jest.Mock<{}>).mockReturnValueOnce(true) + const enabled = true; + const sut = new CoverageOverlay(null, coverageMapProvider, enabled); + ((hasDocument as unknown) as jest.Mock<{}>).mockReturnValueOnce(true); - const editor: any = {} - sut.update(editor) + const editor: any = {}; + sut.update(editor); - expect(sut.formatter.format).toBeCalledWith(editor) - }) + expect(sut.formatter.format).toBeCalledWith(editor); + }); it('should remove the overlay when disabled', () => { - const enabled = false - const sut = new CoverageOverlay(null, coverageMapProvider, enabled) - ;((hasDocument as unknown) as jest.Mock<{}>).mockReturnValueOnce(true) + const enabled = false; + const sut = new CoverageOverlay(null, coverageMapProvider, enabled); + ((hasDocument as unknown) as jest.Mock<{}>).mockReturnValueOnce(true); - const editor: any = {} - sut.update(editor) + const editor: any = {}; + sut.update(editor); - expect(sut.formatter.clear).toBeCalledWith(editor) - }) - }) -}) + expect(sut.formatter.clear).toBeCalledWith(editor); + }); + }); +}); diff --git a/tests/Coverage/Formatters/DefaultFormatter.test.ts b/tests/Coverage/Formatters/DefaultFormatter.test.ts index d8ee3d3f0..013bfb8d7 100644 --- a/tests/Coverage/Formatters/DefaultFormatter.test.ts +++ b/tests/Coverage/Formatters/DefaultFormatter.test.ts @@ -1,5 +1,5 @@ -jest.unmock('../../../src/Coverage/Formatters/DefaultFormatter') -jest.unmock('../../../src/Coverage/Formatters/AbstractFormatter') +jest.unmock('../../../src/Coverage/Formatters/DefaultFormatter'); +jest.unmock('../../../src/Coverage/Formatters/AbstractFormatter'); jest.mock('vscode', () => { return { @@ -8,67 +8,67 @@ jest.mock('vscode', () => { window: { createTextEditorDecorationType: jest.fn(), }, - } -}) + }; +}); -import { DefaultFormatter } from '../../../src/Coverage/Formatters/DefaultFormatter' -import * as vscode from 'vscode' -import { isValidLocation } from '../../../src/Coverage/Formatters/helpers' +import { DefaultFormatter } from '../../../src/Coverage/Formatters/DefaultFormatter'; +import * as vscode from 'vscode'; +import { isValidLocation } from '../../../src/Coverage/Formatters/helpers'; describe('DefaultFormatter', () => { describe('format()', () => { - const fileCoverage: any = {} + const fileCoverage: any = {}; const coverageMapProvider: any = { getFileCoverage: jest.fn().mockReturnValue(fileCoverage), - } + }; const editor: any = { document: { fileName: {}, }, - } + }; - let sut + let sut; beforeEach(() => { - sut = new DefaultFormatter(coverageMapProvider) - sut.formatBranches = jest.fn() - sut.formatUncoveredLines = jest.fn() - }) + sut = new DefaultFormatter(coverageMapProvider); + sut.formatBranches = jest.fn(); + sut.formatUncoveredLines = jest.fn(); + }); it('should do nothing when the file coverage is not found', () => { - coverageMapProvider.getFileCoverage.mockReturnValueOnce() - sut.format(editor) + coverageMapProvider.getFileCoverage.mockReturnValueOnce(); + sut.format(editor); - expect(sut.formatBranches).not.toBeCalled() - expect(sut.formatUncoveredLines).not.toBeCalled() - }) + expect(sut.formatBranches).not.toBeCalled(); + expect(sut.formatUncoveredLines).not.toBeCalled(); + }); it('should get the file coverage from the coverage provider', () => { - sut.format(editor) + sut.format(editor); - expect(coverageMapProvider.getFileCoverage).toBeCalledWith(editor.document.fileName) - }) + expect(coverageMapProvider.getFileCoverage).toBeCalledWith(editor.document.fileName); + }); it('should add the overlay for uncovered branches', () => { - sut.format(editor) + sut.format(editor); - expect(sut.formatBranches).toBeCalledWith(editor, fileCoverage) - }) + expect(sut.formatBranches).toBeCalledWith(editor, fileCoverage); + }); it('should add the overlay for uncovered lines', () => { - sut.format(editor) + sut.format(editor); - expect(sut.formatUncoveredLines).toBeCalledWith(editor, fileCoverage) - }) - }) + expect(sut.formatUncoveredLines).toBeCalledWith(editor, fileCoverage); + }); + }); describe('formatBranches()', () => { it('should do nothing when the branch has been hit', () => { - const coverageMapProvider: any = {} - const sut = new DefaultFormatter(coverageMapProvider) + const coverageMapProvider: any = {}; + const sut = new DefaultFormatter(coverageMapProvider); const editor: any = { setDecorations: jest.fn(), - } + }; const fileCoverage: any = { b: { 0: [1], @@ -78,21 +78,21 @@ describe('DefaultFormatter', () => { locations: [{}], }, }, - } - sut.formatBranches(editor, fileCoverage) + }; + sut.formatBranches(editor, fileCoverage); - expect(editor.setDecorations).toBeCalledWith(undefined, []) - }) + expect(editor.setDecorations).toBeCalledWith(undefined, []); + }); it('should do nothing when the branch location is not valid', () => { - ;(isValidLocation as jest.Mock<any>).mockReturnValueOnce(false) + (isValidLocation as jest.Mock<any>).mockReturnValueOnce(false); - const coverageMapProvider: any = {} - const sut = new DefaultFormatter(coverageMapProvider) + const coverageMapProvider: any = {}; + const sut = new DefaultFormatter(coverageMapProvider); const editor: any = { setDecorations: jest.fn(), - } + }; const fileCoverage: any = { b: { 0: [0], @@ -102,19 +102,19 @@ describe('DefaultFormatter', () => { locations: [{}], }, }, - } - sut.formatBranches(editor, fileCoverage) + }; + sut.formatBranches(editor, fileCoverage); - expect(editor.setDecorations).toBeCalledWith(undefined, []) - }) + expect(editor.setDecorations).toBeCalledWith(undefined, []); + }); it('should reindex the line numbers', () => { - ;(isValidLocation as jest.Mock<any>).mockReturnValueOnce(true) + (isValidLocation as jest.Mock<any>).mockReturnValueOnce(true); - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const editor: any = { setDecorations: jest.fn(), - } + }; const fileCoverage: any = { b: { @@ -136,23 +136,25 @@ describe('DefaultFormatter', () => { ], }, }, - } + }; - const sut = new DefaultFormatter(coverageMapProvider) - sut.formatBranches(editor, fileCoverage) + const sut = new DefaultFormatter(coverageMapProvider); + sut.formatBranches(editor, fileCoverage); - expect(vscode.Range).toBeCalledWith(1, 2, 5, 3) - }) + expect(vscode.Range).toBeCalledWith(1, 2, 5, 3); + }); it('should add decorations with the reindexed ranges', () => { - const expected = [{}, {}] - ;((vscode.Range as unknown) as jest.Mock<{}>).mockReturnValueOnce(expected[0]).mockReturnValueOnce(expected[1]) - ;(isValidLocation as jest.Mock<any>).mockReturnValueOnce(true).mockReturnValueOnce(true) + const expected = [{}, {}]; + ((vscode.Range as unknown) as jest.Mock<{}>) + .mockReturnValueOnce(expected[0]) + .mockReturnValueOnce(expected[1]); + (isValidLocation as jest.Mock<any>).mockReturnValueOnce(true).mockReturnValueOnce(true); - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const editor: any = { setDecorations: jest.fn(), - } + }; const location = { start: { @@ -163,7 +165,7 @@ describe('DefaultFormatter', () => { line: 6, column: 3, }, - } + }; const fileCoverage: any = { b: { 0: [0, 0], @@ -173,21 +175,21 @@ describe('DefaultFormatter', () => { locations: [location, location], }, }, - } + }; - const sut = new DefaultFormatter(coverageMapProvider) - sut.formatBranches(editor, fileCoverage) + const sut = new DefaultFormatter(coverageMapProvider); + sut.formatBranches(editor, fileCoverage); - expect(editor.setDecorations).toBeCalledWith(undefined, expected) - }) + expect(editor.setDecorations).toBeCalledWith(undefined, expected); + }); it('should handle when the end column is `null`', () => { - ;(isValidLocation as jest.Mock<any>).mockReturnValueOnce(true) + (isValidLocation as jest.Mock<any>).mockReturnValueOnce(true); - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const editor: any = { setDecorations: jest.fn(), - } + }; const fileCoverage: any = { b: { @@ -209,73 +211,75 @@ describe('DefaultFormatter', () => { ], }, }, - } + }; - const sut = new DefaultFormatter(coverageMapProvider) - sut.formatBranches(editor, fileCoverage) + const sut = new DefaultFormatter(coverageMapProvider); + sut.formatBranches(editor, fileCoverage); - expect(vscode.Range).toBeCalledWith(1, 2, 3, 0) - }) - }) + expect(vscode.Range).toBeCalledWith(1, 2, 3, 0); + }); + }); describe('formatUncoveredLines()', () => { it('should reindex the line numbers', () => { - ;(vscode.Range as jest.Mock<vscode.Range>).mockReset() + (vscode.Range as jest.Mock<vscode.Range>).mockReset(); - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const editor: any = { setDecorations: () => ({}), - } + }; const fileCoverage: any = { getUncoveredLines: () => [1, 10], - } + }; - const sut = new DefaultFormatter(coverageMapProvider) - sut.formatUncoveredLines(editor, fileCoverage) + const sut = new DefaultFormatter(coverageMapProvider); + sut.formatUncoveredLines(editor, fileCoverage); - expect(vscode.Range).toHaveBeenCalledTimes(2) + expect(vscode.Range).toHaveBeenCalledTimes(2); expect((vscode.Range as jest.Mock<vscode.Range>).mock.calls).toEqual([ [0, 0, 0, 0], [9, 0, 9, 0], - ]) - }) + ]); + }); it('should add decorations with the reindexed ranges', () => { - const expected = [{}, {}] - ;((vscode.Range as unknown) as jest.Mock<{}>).mockReturnValueOnce(expected[0]).mockReturnValueOnce(expected[1]) + const expected = [{}, {}]; + ((vscode.Range as unknown) as jest.Mock<{}>) + .mockReturnValueOnce(expected[0]) + .mockReturnValueOnce(expected[1]); - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const editor: any = { setDecorations: jest.fn(), - } + }; const fileCoverage: any = { getUncoveredLines: () => [1, 10], - } + }; - const sut = new DefaultFormatter(coverageMapProvider) - sut.formatUncoveredLines(editor, fileCoverage) + const sut = new DefaultFormatter(coverageMapProvider); + sut.formatUncoveredLines(editor, fileCoverage); - expect(editor.setDecorations).toBeCalledWith(undefined, expected) - }) - }) + expect(editor.setDecorations).toBeCalledWith(undefined, expected); + }); + }); describe('clear()', () => { it('should clear the overlay', () => { - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const editor: any = { setDecorations: jest.fn(), - } + }; - const sut = new DefaultFormatter(coverageMapProvider) - sut.clear(editor) + const sut = new DefaultFormatter(coverageMapProvider); + sut.clear(editor); // Both decoration types are removed by calling setDecorations with an // empty range - expect(editor.setDecorations).toHaveBeenCalledTimes(2) + expect(editor.setDecorations).toHaveBeenCalledTimes(2); for (const args of editor.setDecorations.mock.calls) { - expect(args.length).toBe(2) - expect(args[1]).toEqual([]) + expect(args.length).toBe(2); + expect(args[1]).toEqual([]); } - }) - }) -}) + }); + }); +}); diff --git a/tests/Coverage/Formatters/GutterFormatter.test.ts b/tests/Coverage/Formatters/GutterFormatter.test.ts index 7fba2d9fe..5eb4ec3ad 100644 --- a/tests/Coverage/Formatters/GutterFormatter.test.ts +++ b/tests/Coverage/Formatters/GutterFormatter.test.ts @@ -1,19 +1,19 @@ -jest.unmock('../../../src/Coverage/Formatters/GutterFormatter') -jest.unmock('../../../src/Coverage/Formatters/AbstractFormatter') +jest.unmock('../../../src/Coverage/Formatters/GutterFormatter'); +jest.unmock('../../../src/Coverage/Formatters/AbstractFormatter'); class RangeMock { - public args + public args; constructor(args) { - this.args = args + this.args = args; } public isEqual(range: RangeMock) { for (let i = 0; i < range.args.length; i++) { if (this.args[i] !== range.args[i]) { - return false + return false; } } - return true + return true; } } @@ -24,76 +24,76 @@ jest.mock('vscode', () => { window: { createTextEditorDecorationType: jest.fn(), }, - } -}) + }; +}); -import { GutterFormatter } from '../../../src/Coverage/Formatters/GutterFormatter' -import * as vscode from 'vscode' -import { isValidLocation } from '../../../src/Coverage/Formatters/helpers' +import { GutterFormatter } from '../../../src/Coverage/Formatters/GutterFormatter'; +import * as vscode from 'vscode'; +import { isValidLocation } from '../../../src/Coverage/Formatters/helpers'; describe('GutterFormatter', () => { describe('format()', () => { - const fileCoverage: any = {} + const fileCoverage: any = {}; const coverageMapProvider: any = { getFileCoverage: jest.fn().mockReturnValue(fileCoverage), - } + }; const editor: any = { setDecorations: jest.fn(), document: { fileName: 'targetfile.ts', lineCount: 10, }, - } + }; const context: any = { asAbsolutePath: (path: string) => path, - } + }; - let sut + let sut; beforeEach(() => { - sut = new GutterFormatter(context, coverageMapProvider) + sut = new GutterFormatter(context, coverageMapProvider); sut.computeFormatting = jest.fn().mockReturnValue({ covered: [], partiallyCovered: [], uncovered: [], - }) - }) + }); + }); it('should do nothing when the file coverage is not found', () => { - coverageMapProvider.getFileCoverage.mockReturnValueOnce() - sut.format(editor) + coverageMapProvider.getFileCoverage.mockReturnValueOnce(); + sut.format(editor); - expect(sut.computeFormatting).not.toBeCalled() - }) + expect(sut.computeFormatting).not.toBeCalled(); + }); it('should get the file coverage from the coverage provider', () => { - sut.format(editor) + sut.format(editor); - expect(coverageMapProvider.getFileCoverage).toBeCalledWith(editor.document.fileName) - }) + expect(coverageMapProvider.getFileCoverage).toBeCalledWith(editor.document.fileName); + }); it('should add the coverage', () => { - editor.setDecorations.mockClear() - sut.format(editor) + editor.setDecorations.mockClear(); + sut.format(editor); - expect(sut.computeFormatting).toBeCalledWith(editor, fileCoverage) - expect(editor.setDecorations).toHaveBeenCalledTimes(3) - }) - }) + expect(sut.computeFormatting).toBeCalledWith(editor, fileCoverage); + expect(editor.setDecorations).toHaveBeenCalledTimes(3); + }); + }); describe('computeFormatting()', () => { it('should do nothing when the branch has been hit', () => { - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const context: any = { asAbsolutePath: (path: string) => path, - } - const sut = new GutterFormatter(context, coverageMapProvider) + }; + const sut = new GutterFormatter(context, coverageMapProvider); const editor: any = { setDecorations: jest.fn(), document: { lineCount: 10, }, - } + }; const fileCoverage: any = { getUncoveredLines: () => ['1', '10'], b: { @@ -104,27 +104,27 @@ describe('GutterFormatter', () => { locations: [{}], }, }, - } - const actual = sut.computeFormatting(editor, fileCoverage) + }; + const actual = sut.computeFormatting(editor, fileCoverage); - expect(actual.partiallyCovered).toEqual([]) - }) + expect(actual.partiallyCovered).toEqual([]); + }); it('should do nothing when the branch location is not valid', () => { - ;(isValidLocation as jest.Mock<any>).mockReturnValueOnce(false) + (isValidLocation as jest.Mock<any>).mockReturnValueOnce(false); - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const context: any = { asAbsolutePath: (path: string) => path, - } - const sut = new GutterFormatter(context, coverageMapProvider) + }; + const sut = new GutterFormatter(context, coverageMapProvider); const editor: any = { setDecorations: jest.fn(), document: { lineCount: 10, }, - } + }; const fileCoverage: any = { getUncoveredLines: () => ['1', '10'], b: { @@ -135,25 +135,25 @@ describe('GutterFormatter', () => { locations: [{}], }, }, - } - const actual = sut.computeFormatting(editor, fileCoverage) + }; + const actual = sut.computeFormatting(editor, fileCoverage); - expect(actual.partiallyCovered).toEqual([]) - }) + expect(actual.partiallyCovered).toEqual([]); + }); it('should reindex the line numbers for partially covered', () => { - ;(isValidLocation as jest.Mock<any>).mockReturnValueOnce(true) + (isValidLocation as jest.Mock<any>).mockReturnValueOnce(true); - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const editor: any = { setDecorations: jest.fn(), document: { lineCount: 10, }, - } + }; const context: any = { asAbsolutePath: (path: string) => path, - } + }; const fileCoverage: any = { getUncoveredLines: () => ['1', '10'], @@ -176,27 +176,27 @@ describe('GutterFormatter', () => { ], }, }, - } + }; - const sut = new GutterFormatter(context, coverageMapProvider) - const actual = sut.computeFormatting(editor, fileCoverage) + const sut = new GutterFormatter(context, coverageMapProvider); + const actual = sut.computeFormatting(editor, fileCoverage); - expect(actual.partiallyCovered).toEqual([new vscode.Range(1, 2, 5, 3)]) - }) + expect(actual.partiallyCovered).toEqual([new vscode.Range(1, 2, 5, 3)]); + }); it('should add decorations with the reindexed ranges', () => { - ;(isValidLocation as jest.Mock<any>).mockReturnValueOnce(true).mockReturnValueOnce(true) + (isValidLocation as jest.Mock<any>).mockReturnValueOnce(true).mockReturnValueOnce(true); - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const editor: any = { setDecorations: jest.fn(), document: { lineCount: 10, }, - } + }; const context: any = { asAbsolutePath: (path: string) => path, - } + }; const location = { start: { @@ -207,7 +207,7 @@ describe('GutterFormatter', () => { line: 6, column: 3, }, - } + }; const fileCoverage: any = { getUncoveredLines: () => ['1', '10'], b: { @@ -218,61 +218,64 @@ describe('GutterFormatter', () => { locations: [location, location], }, }, - } + }; - const sut = new GutterFormatter(context, coverageMapProvider) - const actual = sut.computeFormatting(editor, fileCoverage) + const sut = new GutterFormatter(context, coverageMapProvider); + const actual = sut.computeFormatting(editor, fileCoverage); - expect(actual.partiallyCovered).toEqual([new vscode.Range(1, 2, 5, 3)]) - }) + expect(actual.partiallyCovered).toEqual([new vscode.Range(1, 2, 5, 3)]); + }); it('should reindex the line numbers for uncovered', () => { - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const editor: any = { setDecorations: () => ({}), document: { lineCount: 10, }, - } + }; const context: any = { asAbsolutePath: (path: string) => path, - } + }; const fileCoverage: any = { getUncoveredLines: () => ['1', '10'], b: {}, branchMap: {}, - } + }; - const sut = new GutterFormatter(context, coverageMapProvider) - const actual = sut.computeFormatting(editor, fileCoverage) + const sut = new GutterFormatter(context, coverageMapProvider); + const actual = sut.computeFormatting(editor, fileCoverage); - expect(actual.uncovered).toEqual([new vscode.Range(0, 0, 0, 0), new vscode.Range(9, 0, 9, 0)]) - }) - }) + expect(actual.uncovered).toEqual([ + new vscode.Range(0, 0, 0, 0), + new vscode.Range(9, 0, 9, 0), + ]); + }); + }); describe('clear()', () => { it('should clear the overlay', () => { - const coverageMapProvider: any = {} + const coverageMapProvider: any = {}; const editor: any = { setDecorations: jest.fn(), document: { lineCount: 10, }, - } + }; const context: any = { asAbsolutePath: (path: string) => path, - } + }; - const sut = new GutterFormatter(context, coverageMapProvider) - sut.clear(editor) + const sut = new GutterFormatter(context, coverageMapProvider); + sut.clear(editor); // All decoration types are removed by calling setDecorations with an // empty range - expect(editor.setDecorations).toHaveBeenCalledTimes(3) + expect(editor.setDecorations).toHaveBeenCalledTimes(3); for (const args of editor.setDecorations.mock.calls) { - expect(args.length).toBe(2) - expect(args[1]).toEqual([]) + expect(args.length).toBe(2); + expect(args[1]).toEqual([]); } - }) - }) -}) + }); + }); +}); diff --git a/tests/Coverage/Formatters/helpers.test.ts b/tests/Coverage/Formatters/helpers.test.ts index 256461686..ef64cc680 100644 --- a/tests/Coverage/Formatters/helpers.test.ts +++ b/tests/Coverage/Formatters/helpers.test.ts @@ -1,55 +1,55 @@ -jest.unmock('../../../src/Coverage/Formatters/helpers') -import { isValidPosition, isValidLocation } from '../../../src/Coverage/Formatters/helpers' +jest.unmock('../../../src/Coverage/Formatters/helpers'); +import { isValidPosition, isValidLocation } from '../../../src/Coverage/Formatters/helpers'; describe('Coverage Formatters helpers', () => { describe('isValidPosition()', () => { it('should return false when the position is falsy', () => { - expect(isValidPosition(undefined)).toBe(false) - }) + expect(isValidPosition(undefined)).toBe(false); + }); it('should return false when the line number is undefined', () => { - const position: any = {} - expect(isValidPosition(position)).toBe(false) - }) + const position: any = {}; + expect(isValidPosition(position)).toBe(false); + }); it('should return false when the line number is null', () => { - const position: any = { line: null } - expect(isValidPosition(position)).toBe(false) - }) + const position: any = { line: null }; + expect(isValidPosition(position)).toBe(false); + }); it('should return false when the line number is less than zero', () => { - const position: any = { line: -1 } - expect(isValidPosition(position)).toBe(false) - }) + const position: any = { line: -1 }; + expect(isValidPosition(position)).toBe(false); + }); it('should return false when the line number is zero or more', () => { - let position: any = { line: 0 } - expect(isValidPosition(position)).toBe(true) + let position: any = { line: 0 }; + expect(isValidPosition(position)).toBe(true); - position = { line: 1 } - expect(isValidPosition(position)).toBe(true) - }) - }) + position = { line: 1 }; + expect(isValidPosition(position)).toBe(true); + }); + }); describe('isValidLocation()', () => { it('should return false when the start is not valid', () => { - const location: any = {} - expect(isValidLocation(location)).toBe(false) - }) + const location: any = {}; + expect(isValidLocation(location)).toBe(false); + }); it('should return false when the end is not valid', () => { const location: any = { start: { line: 2 }, - } - expect(isValidLocation(location)).toBe(false) - }) + }; + expect(isValidLocation(location)).toBe(false); + }); it('should return true when the start and end positions are valid', () => { const location: any = { start: { line: 2 }, end: { line: 6 }, - } - expect(isValidLocation(location)).toBe(true) - }) - }) -}) + }; + expect(isValidLocation(location)).toBe(true); + }); + }); +}); diff --git a/tests/DebugCodeLens/DebugCodeLens.test.ts b/tests/DebugCodeLens/DebugCodeLens.test.ts index 752efaae2..b31a2110b 100644 --- a/tests/DebugCodeLens/DebugCodeLens.test.ts +++ b/tests/DebugCodeLens/DebugCodeLens.test.ts @@ -1,34 +1,34 @@ -jest.unmock('../../src/DebugCodeLens/DebugCodeLens') +jest.unmock('../../src/DebugCodeLens/DebugCodeLens'); jest.mock('vscode', () => ({ CodeLens: class { // tslint:disable-next-line no-empty constructor() {} }, -})) +})); -import { DebugCodeLens } from '../../src/DebugCodeLens/DebugCodeLens' -import * as vscode from 'vscode' +import { DebugCodeLens } from '../../src/DebugCodeLens/DebugCodeLens'; +import * as vscode from 'vscode'; describe('DebugCodeLens', () => { - const document = {} as any - const range = {} as any - const fileName = 'file.js' - const testName = 'should specify the test file name' - const sut = new DebugCodeLens(document, range, fileName, testName) + const document = {} as any; + const range = {} as any; + const fileName = 'file.js'; + const testName = 'should specify the test file name'; + const sut = new DebugCodeLens(document, range, fileName, testName); it('should extend vscode.CodeLens', () => { - expect(sut).toBeInstanceOf(vscode.CodeLens) - }) + expect(sut).toBeInstanceOf(vscode.CodeLens); + }); it('should specify the file document', () => { - expect(sut.document).toBe(document) - }) + expect(sut.document).toBe(document); + }); it('should specify the file name', () => { - expect(sut.fileName).toBe(fileName) - }) + expect(sut.fileName).toBe(fileName); + }); it('should specify the test name', () => { - expect(sut.testName).toBe(testName) - }) -}) + expect(sut.testName).toBe(testName); + }); +}); diff --git a/tests/DebugCodeLens/DebugCodeLensProvider.test.ts b/tests/DebugCodeLens/DebugCodeLensProvider.test.ts index ada5f1b2e..809f99309 100644 --- a/tests/DebugCodeLens/DebugCodeLensProvider.test.ts +++ b/tests/DebugCodeLens/DebugCodeLensProvider.test.ts @@ -1,16 +1,16 @@ -jest.unmock('../../src/DebugCodeLens/DebugCodeLensProvider') -jest.unmock('../../src/DebugCodeLens/DebugCodeLens') -jest.unmock('../../src/helpers') -jest.mock('path') +jest.unmock('../../src/DebugCodeLens/DebugCodeLensProvider'); +jest.unmock('../../src/DebugCodeLens/DebugCodeLens'); +jest.unmock('../../src/helpers'); +jest.mock('path'); // tslint:disable max-classes-per-file -const rangeConstructor = jest.fn() +const rangeConstructor = jest.fn(); jest.mock('vscode', () => { class CodeLens { - range: any + range: any; constructor(range) { - this.range = range + this.range = range; } } @@ -19,23 +19,23 @@ jest.mock('vscode', () => { } class Position { - lineNumber: string - character: string + lineNumber: string; + character: string; constructor(lineNumber, character) { - this.lineNumber = lineNumber - this.character = character + this.lineNumber = lineNumber; + this.character = character; } } class Range { - start: Position - end: Position + start: Position; + end: Position; constructor(start, end) { - rangeConstructor() - this.start = start - this.end = end + rangeConstructor(); + this.start = start; + this.end = end; } } @@ -44,85 +44,87 @@ jest.mock('vscode', () => { EventEmitter, Position, Range, - } -}) + }; +}); -import { DebugCodeLensProvider } from '../../src/DebugCodeLens/DebugCodeLensProvider' -import { TestResultProvider, TestResult, TestReconciliationState } from '../../src/TestResults' -import { DebugCodeLens } from '../../src/DebugCodeLens/DebugCodeLens' -import { extensionName } from '../../src/appGlobals' -import { basename } from 'path' -import * as vscode from 'vscode' -import { TestState } from '../../src/DebugCodeLens' +import { DebugCodeLensProvider } from '../../src/DebugCodeLens/DebugCodeLensProvider'; +import { TestResultProvider, TestResult, TestReconciliationState } from '../../src/TestResults'; +import { DebugCodeLens } from '../../src/DebugCodeLens/DebugCodeLens'; +import { extensionName } from '../../src/appGlobals'; +import { basename } from 'path'; +import * as vscode from 'vscode'; +import { TestState } from '../../src/DebugCodeLens'; describe('DebugCodeLensProvider', () => { - const testResultProvider = new TestResultProvider() - const provideJestExt: any = () => ({ testResultProvider }) - const allTestStates = [TestState.Fail, TestState.Pass, TestState.Skip, TestState.Unknown] + const testResultProvider = new TestResultProvider(); + const provideJestExt: any = () => ({ testResultProvider }); + const allTestStates = [TestState.Fail, TestState.Pass, TestState.Skip, TestState.Unknown]; describe('constructor()', () => { it('should set the jest extension provider', () => { - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); - expect((sut as any).getJestExt().testResultProvider).toBe(testResultProvider) - }) + expect((sut as any).getJestExt().testResultProvider).toBe(testResultProvider); + }); it('should set which test states to show the CodeLens above', () => { - expect(new DebugCodeLensProvider(provideJestExt, allTestStates).showWhenTestStateIn).toBe(allTestStates) + expect(new DebugCodeLensProvider(provideJestExt, allTestStates).showWhenTestStateIn).toBe( + allTestStates + ); - const none = [] - expect(new DebugCodeLensProvider(provideJestExt, none).showWhenTestStateIn).toBe(none) - }) + const none = []; + expect(new DebugCodeLensProvider(provideJestExt, none).showWhenTestStateIn).toBe(none); + }); it('should initialize the onChange event emitter', () => { - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); - expect(sut.onDidChange).toBeInstanceOf(vscode.EventEmitter) - }) - }) + expect(sut.onDidChange).toBeInstanceOf(vscode.EventEmitter); + }); + }); describe('showWhenTestStateIn', () => { describe('get', () => { it('should return which test states to show the CodeLens above', () => { for (const states of [[], allTestStates]) { - const sut = new DebugCodeLensProvider(provideJestExt, states) - expect(sut.showWhenTestStateIn).toBe(states) + const sut = new DebugCodeLensProvider(provideJestExt, states); + expect(sut.showWhenTestStateIn).toBe(states); } - }) - }) + }); + }); describe('set', () => { it('should set which test states to show the CodeLens above', () => { - const sut = new DebugCodeLensProvider(provideJestExt, []) - sut.showWhenTestStateIn = allTestStates + const sut = new DebugCodeLensProvider(provideJestExt, []); + sut.showWhenTestStateIn = allTestStates; - expect(sut.showWhenTestStateIn).toBe(allTestStates) - }) + expect(sut.showWhenTestStateIn).toBe(allTestStates); + }); it('should fire an onDidChange event', () => { - const sut = new DebugCodeLensProvider(provideJestExt, []) - sut.onDidChange.fire = jest.fn() - sut.showWhenTestStateIn = allTestStates + const sut = new DebugCodeLensProvider(provideJestExt, []); + sut.onDidChange.fire = jest.fn(); + sut.showWhenTestStateIn = allTestStates; - expect(sut.onDidChange.fire).toBeCalled() - }) - }) - }) + expect(sut.onDidChange.fire).toBeCalled(); + }); + }); + }); describe('onDidChangeCodeLenses', () => { it('should return the onDidChange event', () => { - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) - const expected = {} as any - sut.onDidChange.event = expected + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); + const expected = {} as any; + sut.onDidChange.event = expected; - expect(sut.onDidChangeCodeLenses).toBe(expected) - }) - }) + expect(sut.onDidChangeCodeLenses).toBe(expected); + }); + }); describe('provideCodeLenses()', () => { - const document = { fileName: 'file.js' } as any - const token = {} as any - const getResults = (testResultProvider.getResults as unknown) as jest.Mock<{}> + const document = { fileName: 'file.js' } as any; + const token = {} as any; + const getResults = (testResultProvider.getResults as unknown) as jest.Mock<{}>; const testResults = [ ({ name: 'should fail', @@ -136,150 +138,150 @@ describe('DebugCodeLensProvider', () => { }, status: TestReconciliationState.KnownFail, } as any) as TestResult, - ] + ]; it('should return an empty array when the provider is disabled', () => { - const sut = new DebugCodeLensProvider(provideJestExt, []) + const sut = new DebugCodeLensProvider(provideJestExt, []); - expect(sut.provideCodeLenses(document, token)).toEqual([]) - }) + expect(sut.provideCodeLenses(document, token)).toEqual([]); + }); it('should return an empty array when the document is untitled', () => { - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) - const untitled = { isUntitled: true } as any + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); + const untitled = { isUntitled: true } as any; - expect(sut.provideCodeLenses(untitled, token)).toEqual([]) - }) + expect(sut.provideCodeLenses(untitled, token)).toEqual([]); + }); it('should get the test results for the current document', () => { - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) - getResults.mockReturnValueOnce([]) - sut.provideCodeLenses(document, token) + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); + getResults.mockReturnValueOnce([]); + sut.provideCodeLenses(document, token); - expect(testResultProvider.getResults).toBeCalledWith(document.fileName) - }) + expect(testResultProvider.getResults).toBeCalledWith(document.fileName); + }); it('should not show the CodeLens above failing tests unless configured', () => { - const testStates = allTestStates.filter((s) => s !== TestState.Fail) - const status = TestReconciliationState.KnownFail - const sut = new DebugCodeLensProvider(provideJestExt, testStates) - getResults.mockReturnValueOnce([{ status }]) + const testStates = allTestStates.filter((s) => s !== TestState.Fail); + const status = TestReconciliationState.KnownFail; + const sut = new DebugCodeLensProvider(provideJestExt, testStates); + getResults.mockReturnValueOnce([{ status }]); - expect(sut.provideCodeLenses(document, token)).toEqual([]) - }) + expect(sut.provideCodeLenses(document, token)).toEqual([]); + }); it('should not show the CodeLens above passing tests unless configured', () => { - const testStates = allTestStates.filter((s) => s !== TestState.Pass) - const status = TestReconciliationState.KnownSuccess - const sut = new DebugCodeLensProvider(provideJestExt, testStates) - getResults.mockReturnValueOnce([{ status }]) + const testStates = allTestStates.filter((s) => s !== TestState.Pass); + const status = TestReconciliationState.KnownSuccess; + const sut = new DebugCodeLensProvider(provideJestExt, testStates); + getResults.mockReturnValueOnce([{ status }]); - expect(sut.provideCodeLenses(document, token)).toEqual([]) - }) + expect(sut.provideCodeLenses(document, token)).toEqual([]); + }); it('should not show the CodeLens above skipped tests unless configured', () => { - const testStates = allTestStates.filter((s) => s !== TestState.Skip) - const status = TestReconciliationState.KnownSkip - const sut = new DebugCodeLensProvider(provideJestExt, testStates) - getResults.mockReturnValueOnce([{ status }]) + const testStates = allTestStates.filter((s) => s !== TestState.Skip); + const status = TestReconciliationState.KnownSkip; + const sut = new DebugCodeLensProvider(provideJestExt, testStates); + getResults.mockReturnValueOnce([{ status }]); - expect(sut.provideCodeLenses(document, token)).toEqual([]) - }) + expect(sut.provideCodeLenses(document, token)).toEqual([]); + }); it('should not show the CodeLens above unknown tests unless configured', () => { - const testStates = allTestStates.filter((s) => s !== TestState.Unknown) - const status = TestReconciliationState.Unknown - const sut = new DebugCodeLensProvider(provideJestExt, testStates) - getResults.mockReturnValueOnce([{ status }]) + const testStates = allTestStates.filter((s) => s !== TestState.Unknown); + const status = TestReconciliationState.Unknown; + const sut = new DebugCodeLensProvider(provideJestExt, testStates); + getResults.mockReturnValueOnce([{ status }]); - expect(sut.provideCodeLenses(document, token)).toEqual([]) - }) + expect(sut.provideCodeLenses(document, token)).toEqual([]); + }); it('should create the CodeLens at the start of the `test`/`it` block', () => { - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) - getResults.mockReturnValueOnce(testResults) - const actual = sut.provideCodeLenses(document, token) + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); + getResults.mockReturnValueOnce(testResults); + const actual = sut.provideCodeLenses(document, token); - expect(actual).toHaveLength(1) + expect(actual).toHaveLength(1); expect(actual[0].range.start).toEqual({ lineNumber: 1, character: 2, - }) + }); expect(actual[0].range.end).toEqual({ lineNumber: 3, character: 2 + 5, - }) - }) + }); + }); it('should create the CodeLens specifying the document filename', () => { - const expected = 'expected' - ;((basename as unknown) as jest.Mock<{}>).mockReturnValueOnce(expected) - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) - getResults.mockReturnValueOnce(testResults) - const actual = sut.provideCodeLenses(document, token) + const expected = 'expected'; + ((basename as unknown) as jest.Mock<{}>).mockReturnValueOnce(expected); + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); + getResults.mockReturnValueOnce(testResults); + const actual = sut.provideCodeLenses(document, token); - expect(actual).toHaveLength(1) - expect((actual[0] as DebugCodeLens).fileName).toBe(expected) - }) + expect(actual).toHaveLength(1); + expect((actual[0] as DebugCodeLens).fileName).toBe(expected); + }); it('should create the CodeLens specifying the test name', () => { - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) - getResults.mockReturnValueOnce(testResults) - const actual = sut.provideCodeLenses(document, token) + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); + getResults.mockReturnValueOnce(testResults); + const actual = sut.provideCodeLenses(document, token); - expect(actual).toHaveLength(1) - expect((actual[0] as DebugCodeLens).testName).toBe(testResults[0].name) - }) - }) + expect(actual).toHaveLength(1); + expect((actual[0] as DebugCodeLens).testName).toBe(testResults[0].name); + }); + }); describe('resolveCodeLenses()', () => { it('should add the command to a DebugCodeLenses', () => { - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) - const document = {} as any - const range = {} as any - const fileName = 'fileName' - const testName = 'testName' - const codeLens = new DebugCodeLens(document, range, fileName, testName) - const token = {} as any - sut.resolveCodeLens(codeLens, token) + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); + const document = {} as any; + const range = {} as any; + const fileName = 'fileName'; + const testName = 'testName'; + const codeLens = new DebugCodeLens(document, range, fileName, testName); + const token = {} as any; + sut.resolveCodeLens(codeLens, token); expect(codeLens.command).toEqual({ arguments: [document, fileName, testName], command: `${extensionName}.run-test`, title: 'Debug', - }) - }) + }); + }); it('should escape testName for regex', () => { - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) - const document = {} as any - const range = {} as any - const fileName = 'fileName' - const testName = 'testName()' - const codeLens = new DebugCodeLens(document, range, fileName, testName) - const token = {} as any - sut.resolveCodeLens(codeLens, token) + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); + const document = {} as any; + const range = {} as any; + const fileName = 'fileName'; + const testName = 'testName()'; + const codeLens = new DebugCodeLens(document, range, fileName, testName); + const token = {} as any; + sut.resolveCodeLens(codeLens, token); expect(codeLens.command).toEqual({ arguments: [document, fileName, 'testName\\(\\)'], command: `${extensionName}.run-test`, title: 'Debug', - }) - }) + }); + }); it('should leave other CodeLenses unchanged', () => { - const sut = new DebugCodeLensProvider(provideJestExt, []) - const codeLens = {} as any - const token = {} as any - sut.resolveCodeLens(codeLens, token) + const sut = new DebugCodeLensProvider(provideJestExt, []); + const codeLens = {} as any; + const token = {} as any; + sut.resolveCodeLens(codeLens, token); - expect(codeLens.command).toBeUndefined() - }) - }) + expect(codeLens.command).toBeUndefined(); + }); + }); it('didChange()', () => { - const sut = new DebugCodeLensProvider(provideJestExt, allTestStates) - sut.onDidChange.fire = jest.fn() - sut.didChange() + const sut = new DebugCodeLensProvider(provideJestExt, allTestStates); + sut.onDidChange.fire = jest.fn(); + sut.didChange(); - expect(sut.onDidChange.fire).toBeCalled() - }) -}) + expect(sut.onDidChange.fire).toBeCalled(); + }); +}); diff --git a/tests/DebugConfigurationProvider.test.ts b/tests/DebugConfigurationProvider.test.ts index 4b8fe85be..90c2c73a5 100644 --- a/tests/DebugConfigurationProvider.test.ts +++ b/tests/DebugConfigurationProvider.test.ts @@ -1,53 +1,55 @@ -jest.unmock('../src/DebugConfigurationProvider') +jest.unmock('../src/DebugConfigurationProvider'); -import { DebugConfigurationProvider } from '../src/DebugConfigurationProvider' -import { getTestCommand, isCreateReactAppTestCommand } from '../src/helpers' +import { DebugConfigurationProvider } from '../src/DebugConfigurationProvider'; +import { getTestCommand, isCreateReactAppTestCommand } from '../src/helpers'; describe('DebugConfigurationProvider', () => { it('should by default return a DebugConfiguration for Jest', () => { - const folder: any = { uri: { fsPath: null } } - const sut = new DebugConfigurationProvider() - const configurations = sut.provideDebugConfigurations(folder) - - expect(configurations).toHaveLength(1) - const config = configurations[0] - expect(config.name).toBe('vscode-jest-tests') - expect(config.type).toBe('node') - expect(config.args).toContain('--runInBand') - expect(config.program).toMatch('jest') - }) + const folder: any = { uri: { fsPath: null } }; + const sut = new DebugConfigurationProvider(); + const configurations = sut.provideDebugConfigurations(folder); + + expect(configurations).toHaveLength(1); + const config = configurations[0]; + expect(config.name).toBe('vscode-jest-tests'); + expect(config.type).toBe('node'); + expect(config.args).toContain('--runInBand'); + expect(config.program).toMatch('jest'); + }); it('should return a valid CRA DebugConfiguration', () => { - ;((getTestCommand as unknown) as jest.Mock<{}>).mockReturnValueOnce('react-scripts test --env=jsdom') - ;((isCreateReactAppTestCommand as unknown) as jest.Mock<{}>).mockReturnValueOnce(true) - - const folder: any = { uri: { fsPath: null } } - const sut = new DebugConfigurationProvider() - const configurations = sut.provideDebugConfigurations(folder) - - expect(configurations).toHaveLength(1) - const config = configurations[0] - expect(config.name).toBe('vscode-jest-tests') - expect(config.type).toBe('node') + ((getTestCommand as unknown) as jest.Mock<{}>).mockReturnValueOnce( + 'react-scripts test --env=jsdom' + ); + ((isCreateReactAppTestCommand as unknown) as jest.Mock<{}>).mockReturnValueOnce(true); + + const folder: any = { uri: { fsPath: null } }; + const sut = new DebugConfigurationProvider(); + const configurations = sut.provideDebugConfigurations(folder); + + expect(configurations).toHaveLength(1); + const config = configurations[0]; + expect(config.name).toBe('vscode-jest-tests'); + expect(config.type).toBe('node'); // tslint:disable-next-line no-invalid-template-strings - expect(config.runtimeExecutable).toBe('${workspaceFolder}/node_modules/.bin/react-scripts') - expect(config.args[0]).toBe('test') - expect(config.args).toContain('--env=jsdom') - expect(config.args).toContain('--runInBand') - }) + expect(config.runtimeExecutable).toBe('${workspaceFolder}/node_modules/.bin/react-scripts'); + expect(config.args[0]).toBe('test'); + expect(config.args).toContain('--env=jsdom'); + expect(config.args).toContain('--runInBand'); + }); it('should append the specified tests', () => { - const fileName = 'fileName' - const testNamePattern = 'testNamePattern' - const expected = [fileName, '--testNamePattern', testNamePattern] - let configuration: any = { name: 'vscode-jest-tests' } + const fileName = 'fileName'; + const testNamePattern = 'testNamePattern'; + const expected = [fileName, '--testNamePattern', testNamePattern]; + let configuration: any = { name: 'vscode-jest-tests' }; - const sut = new DebugConfigurationProvider() - sut.prepareTestRun(fileName, testNamePattern) + const sut = new DebugConfigurationProvider(); + sut.prepareTestRun(fileName, testNamePattern); - configuration = sut.resolveDebugConfiguration(undefined, configuration) + configuration = sut.resolveDebugConfiguration(undefined, configuration); - expect(configuration).toBeDefined() - expect(configuration.env && configuration.env.CI).toBeTruthy() - expect(configuration.args).toEqual(expected) - }) -}) + expect(configuration).toBeDefined(); + expect(configuration.env && configuration.env.CI).toBeTruthy(); + expect(configuration.args).toEqual(expected); + }); +}); diff --git a/tests/Jest/index.test.ts b/tests/Jest/index.test.ts index f7e896640..8f0b92d7b 100644 --- a/tests/Jest/index.test.ts +++ b/tests/Jest/index.test.ts @@ -1,11 +1,11 @@ -jest.unmock('../../src/Jest') -import { isWatchNotSupported } from '../../src/Jest' +jest.unmock('../../src/Jest'); +import { isWatchNotSupported } from '../../src/Jest'; describe('isWatchNotSupported', () => { it('returns true when matching the expected message', () => { - const str = '\n--watch is not supported without git/hg, please use --watchAll \n' - expect(isWatchNotSupported(str)).toBe(true) - }) + const str = '\n--watch is not supported without git/hg, please use --watchAll \n'; + expect(isWatchNotSupported(str)).toBe(true); + }); it('returns true when matching an "out of the repository" message', () => { const str = ` @@ -13,11 +13,11 @@ describe('isWatchNotSupported', () => { ● Test suite failed to run - fatal: ../packages/a-dependency-outside-the-submodule: '../packages/a-dependency-outside-the-submodule' is outside repository` - expect(isWatchNotSupported(str)).toBe(true) - }) + fatal: ../packages/a-dependency-outside-the-submodule: '../packages/a-dependency-outside-the-submodule' is outside repository`; + expect(isWatchNotSupported(str)).toBe(true); + }); it('returns false otherwise', () => { - expect(isWatchNotSupported()).toBe(false) - }) -}) + expect(isWatchNotSupported()).toBe(false); + }); +}); diff --git a/tests/JestExt.test.ts b/tests/JestExt.test.ts index b190130cb..ff19096b7 100644 --- a/tests/JestExt.test.ts +++ b/tests/JestExt.test.ts @@ -1,63 +1,63 @@ -jest.unmock('events') -jest.unmock('../src/JestExt') +jest.unmock('events'); +jest.unmock('../src/JestExt'); jest.mock('../src/helpers', () => ({ cleanAnsi: (str: string) => str, pathToJest: jest.fn(), pathToConfig: jest.fn(), -})) +})); jest.mock('../src/DebugCodeLens', () => ({ DebugCodeLensProvider: class MockCodeLensProvider {}, -})) -jest.mock('os') -jest.mock('../src/decorations') +})); +jest.mock('os'); +jest.mock('../src/decorations'); -const update = jest.fn() +const update = jest.fn(); const statusBar = { bind: () => ({ update }), -} -jest.mock('../src/StatusBar', () => ({ statusBar })) - -import { JestExt } from '../src/JestExt' -import { ProjectWorkspace } from 'jest-editor-support' -import { window, workspace, debug } from 'vscode' -import { hasDocument, isOpenInMultipleEditors } from '../src/editor' -import * as decorations from '../src/decorations' -import { updateCurrentDiagnostics } from '../src/diagnostics' -import { JestProcessManager, JestProcess } from '../src/JestProcessManagement' -import * as messaging from '../src/messaging' +}; +jest.mock('../src/StatusBar', () => ({ statusBar })); + +import { JestExt } from '../src/JestExt'; +import { ProjectWorkspace } from 'jest-editor-support'; +import { window, workspace, debug } from 'vscode'; +import { hasDocument, isOpenInMultipleEditors } from '../src/editor'; +import * as decorations from '../src/decorations'; +import { updateCurrentDiagnostics } from '../src/diagnostics'; +import { JestProcessManager, JestProcess } from '../src/JestProcessManagement'; +import * as messaging from '../src/messaging'; /* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "expectItTakesNoAction"] }] */ describe('JestExt', () => { - const getConfiguration = workspace.getConfiguration as jest.Mock<any> - const workspaceFolder = { name: 'test-folder' } as any - let projectWorkspace: ProjectWorkspace - const channelStub = { appendLine: jest.fn(), clear: jest.fn(), show: jest.fn() } as any - const extensionSettings = { debugCodeLens: {} } as any - const debugCodeLensProvider = {} as any + const getConfiguration = workspace.getConfiguration as jest.Mock<any>; + const workspaceFolder = { name: 'test-folder' } as any; + let projectWorkspace: ProjectWorkspace; + const channelStub = { appendLine: jest.fn(), clear: jest.fn(), show: jest.fn() } as any; + const extensionSettings = { debugCodeLens: {} } as any; + const debugCodeLensProvider = {} as any; const debugConfigurationProvider = { provideDebugConfigurations: jest.fn(), prepareTestRun: jest.fn(), - } as any + } as any; - console.error = jest.fn() - console.warn = jest.fn() + console.error = jest.fn(); + console.warn = jest.fn(); beforeEach(() => { - jest.resetAllMocks() + jest.resetAllMocks(); - projectWorkspace = new ProjectWorkspace(null, null, null, null) - getConfiguration.mockReturnValue({}) - }) + projectWorkspace = new ProjectWorkspace(null, null, null, null); + getConfiguration.mockReturnValue({}); + }); describe('resetInlineErrorDecorators()', () => { - let sut: JestExt + let sut: JestExt; const editor: any = { document: { fileName: 'file.js' }, setDecorations: jest.fn(), - } - const decorationType: any = { dispose: jest.fn() } + }; + const decorationType: any = { dispose: jest.fn() }; beforeEach(() => { sut = new JestExt( @@ -70,59 +70,59 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) + ); - sut.canUpdateActiveEditor = jest.fn().mockReturnValueOnce(true) - sut.debugCodeLensProvider.didChange = jest.fn() - ;((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValue({}) - ;((sut.testResultProvider.getSortedResults as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + sut.canUpdateActiveEditor = jest.fn().mockReturnValueOnce(true); + sut.debugCodeLensProvider.didChange = jest.fn(); + ((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValue({}); + ((sut.testResultProvider.getSortedResults as unknown) as jest.Mock<{}>).mockReturnValueOnce({ success: [], fail: [], skip: [], unknown: [], - }) - }) + }); + }); it('should initialize the cached decoration types as an empty array', () => { - expect(sut.failingAssertionDecorators[editor.document.fileName]).toBeUndefined() - sut.triggerUpdateActiveEditor(editor) + expect(sut.failingAssertionDecorators[editor.document.fileName]).toBeUndefined(); + sut.triggerUpdateActiveEditor(editor); - expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([]) - expect(isOpenInMultipleEditors).not.toBeCalled() - }) + expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([]); + expect(isOpenInMultipleEditors).not.toBeCalled(); + }); it('should not clear the cached decorations types when the document is open more than once', () => { - ;((isOpenInMultipleEditors as unknown) as jest.Mock<{}>).mockReturnValueOnce(true) + ((isOpenInMultipleEditors as unknown) as jest.Mock<{}>).mockReturnValueOnce(true); sut.failingAssertionDecorators[editor.document.fileName] = { forEach: jest.fn(), - } as any - sut.triggerUpdateActiveEditor(editor) + } as any; + sut.triggerUpdateActiveEditor(editor); - expect(sut.failingAssertionDecorators[editor.document.fileName].forEach).not.toBeCalled() - }) + expect(sut.failingAssertionDecorators[editor.document.fileName].forEach).not.toBeCalled(); + }); it('should dispose of each cached decoration type', () => { - sut.failingAssertionDecorators[editor.document.fileName] = [decorationType] - sut.triggerUpdateActiveEditor(editor) + sut.failingAssertionDecorators[editor.document.fileName] = [decorationType]; + sut.triggerUpdateActiveEditor(editor); - expect(decorationType.dispose).toBeCalled() - }) + expect(decorationType.dispose).toBeCalled(); + }); it('should reset the cached decoration types', () => { - sut.failingAssertionDecorators[editor.document.fileName] = [decorationType] - sut.triggerUpdateActiveEditor(editor) + sut.failingAssertionDecorators[editor.document.fileName] = [decorationType]; + sut.triggerUpdateActiveEditor(editor); - expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([]) - }) - }) + expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([]); + }); + }); describe('generateInlineErrorDecorator()', () => { it('should add the decoration type to the cache', () => { const settings: any = { debugCodeLens: {}, enableInlineErrorMessages: true, - } + }; const sut = new JestExt( null, workspaceFolder, @@ -133,14 +133,16 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) + ); const editor: any = { document: { fileName: 'file.js' }, setDecorations: jest.fn(), - } - const expected = {} - ;((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValueOnce(expected) - sut.canUpdateActiveEditor = jest.fn().mockReturnValueOnce(true) + }; + const expected = {}; + ((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValueOnce( + expected + ); + sut.canUpdateActiveEditor = jest.fn().mockReturnValueOnce(true); sut.testResultProvider.getSortedResults = jest.fn().mockReturnValueOnce({ success: [], fail: [ @@ -150,29 +152,31 @@ describe('JestExt', () => { ], skip: [], unknown: [], - }) - sut.debugCodeLensProvider.didChange = jest.fn() - sut.triggerUpdateActiveEditor(editor) + }); + sut.debugCodeLensProvider.didChange = jest.fn(); + sut.triggerUpdateActiveEditor(editor); - expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([expected]) - }) - }) + expect(sut.failingAssertionDecorators[editor.document.fileName]).toEqual([expected]); + }); + }); describe('runTest()', () => { - const workspaceFolder = {} as any - const fileName = 'fileName' - const testNamePattern = 'testNamePattern' + const workspaceFolder = {} as any; + const fileName = 'fileName'; + const testNamePattern = 'testNamePattern'; it('should run the supplied test', async () => { - const startDebugging = (debug.startDebugging as unknown) as jest.Mock<{}> - ;((startDebugging as unknown) as jest.Mock<{}>).mockImplementation(async (_folder: any, nameOrConfig: any) => { - // trigger fallback to default configuration - if (typeof nameOrConfig === 'string') { - throw null + const startDebugging = (debug.startDebugging as unknown) as jest.Mock<{}>; + ((startDebugging as unknown) as jest.Mock<{}>).mockImplementation( + async (_folder: any, nameOrConfig: any) => { + // trigger fallback to default configuration + if (typeof nameOrConfig === 'string') { + throw null; + } } - }) + ); - const debugConfiguration = { type: 'dummyconfig' } + const debugConfiguration = { type: 'dummyconfig' }; const sut = new JestExt( null, workspaceFolder, @@ -183,25 +187,29 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - ;((sut.debugConfigurationProvider.provideDebugConfigurations as unknown) as jest.Mock<{}>).mockReturnValue([ + ); + ((sut.debugConfigurationProvider + .provideDebugConfigurations as unknown) as jest.Mock<{}>).mockReturnValue([ debugConfiguration, - ]) + ]); - await sut.runTest(workspaceFolder, fileName, testNamePattern) + await sut.runTest(workspaceFolder, fileName, testNamePattern); - expect(debug.startDebugging).toHaveBeenCalledWith(workspaceFolder, debugConfiguration) + expect(debug.startDebugging).toHaveBeenCalledWith(workspaceFolder, debugConfiguration); - const configuration = startDebugging.mock.calls[startDebugging.mock.calls.length - 1][1] - expect(configuration).toBeDefined() - expect(configuration.type).toBe('dummyconfig') + const configuration = startDebugging.mock.calls[startDebugging.mock.calls.length - 1][1]; + expect(configuration).toBeDefined(); + expect(configuration.type).toBe('dummyconfig'); - expect(sut.debugConfigurationProvider.prepareTestRun).toBeCalledWith(fileName, testNamePattern) - }) - }) + expect(sut.debugConfigurationProvider.prepareTestRun).toBeCalledWith( + fileName, + testNamePattern + ); + }); + }); describe('onDidCloseTextDocument()', () => { - const projectWorkspace = new ProjectWorkspace(null, null, null, null) + const projectWorkspace = new ProjectWorkspace(null, null, null, null); const sut = new JestExt( null, workspaceFolder, @@ -212,24 +220,24 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - const document = {} as any - sut.removeCachedTestResults = jest.fn() - sut.removeCachedDecorationTypes = jest.fn() + ); + const document = {} as any; + sut.removeCachedTestResults = jest.fn(); + sut.removeCachedDecorationTypes = jest.fn(); it('should remove the cached test results', () => { - sut.onDidCloseTextDocument(document) - expect(sut.removeCachedTestResults).toBeCalledWith(document) - }) + sut.onDidCloseTextDocument(document); + expect(sut.removeCachedTestResults).toBeCalledWith(document); + }); it('should remove the cached decorations', () => { - sut.onDidCloseTextDocument(document) - expect(sut.removeCachedDecorationTypes).toBeCalled() - }) - }) + sut.onDidCloseTextDocument(document); + expect(sut.removeCachedDecorationTypes).toBeCalled(); + }); + }); describe('removeCachedTestResults()', () => { - const projectWorkspace = new ProjectWorkspace(null, null, null, null) + const projectWorkspace = new ProjectWorkspace(null, null, null, null); const sut = new JestExt( null, workspaceFolder, @@ -240,31 +248,31 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - sut.testResultProvider.removeCachedResults = jest.fn() + ); + sut.testResultProvider.removeCachedResults = jest.fn(); it('should do nothing when the document is falsy', () => { - sut.removeCachedTestResults(null) - expect(sut.testResultProvider.removeCachedResults).not.toBeCalled() - }) + sut.removeCachedTestResults(null); + expect(sut.testResultProvider.removeCachedResults).not.toBeCalled(); + }); it('should do nothing when the document is untitled', () => { - const document: any = { isUntitled: true } as any - sut.removeCachedTestResults(document) + const document: any = { isUntitled: true } as any; + sut.removeCachedTestResults(document); - expect(sut.testResultProvider.removeCachedResults).not.toBeCalled() - }) + expect(sut.testResultProvider.removeCachedResults).not.toBeCalled(); + }); it('should reset the test result cache for the document', () => { - const expected = 'file.js' - sut.removeCachedTestResults({ fileName: expected } as any) + const expected = 'file.js'; + sut.removeCachedTestResults({ fileName: expected } as any); - expect(sut.testResultProvider.removeCachedResults).toBeCalledWith(expected) - }) - }) + expect(sut.testResultProvider.removeCachedResults).toBeCalledWith(expected); + }); + }); describe('removeCachedAnnotations()', () => { - const projectWorkspace = new ProjectWorkspace(null, null, null, null) + const projectWorkspace = new ProjectWorkspace(null, null, null, null); const sut = new JestExt( null, workspaceFolder, @@ -275,31 +283,31 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) + ); beforeEach(() => { sut.failingAssertionDecorators = { 'file.js': [], - } - }) + }; + }); it('should do nothing when the document is falsy', () => { - sut.onDidCloseTextDocument(null) + sut.onDidCloseTextDocument(null); - expect(sut.failingAssertionDecorators['file.js']).toBeDefined() - }) + expect(sut.failingAssertionDecorators['file.js']).toBeDefined(); + }); it('should remove the annotations for the document', () => { - const document: any = { fileName: 'file.js' } as any - sut.onDidCloseTextDocument(document) + const document: any = { fileName: 'file.js' } as any; + sut.onDidCloseTextDocument(document); - expect(sut.failingAssertionDecorators['file.js']).toBeUndefined() - }) - }) + expect(sut.failingAssertionDecorators['file.js']).toBeUndefined(); + }); + }); describe('onDidChangeActiveTextEditor()', () => { - const editor: any = {} - const projectWorkspace = new ProjectWorkspace(null, null, null, null) + const editor: any = {}; + const projectWorkspace = new ProjectWorkspace(null, null, null, null); const sut = new JestExt( null, workspaceFolder, @@ -310,33 +318,33 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - sut.triggerUpdateActiveEditor = jest.fn() + ); + sut.triggerUpdateActiveEditor = jest.fn(); beforeEach(() => { - ;(sut.triggerUpdateActiveEditor as jest.Mock<{}>).mockReset() - }) + (sut.triggerUpdateActiveEditor as jest.Mock<{}>).mockReset(); + }); it('should update the annotations when the editor has a document', () => { - ;((hasDocument as unknown) as jest.Mock<{}>).mockReturnValueOnce(true) - sut.onDidChangeActiveTextEditor(editor) + ((hasDocument as unknown) as jest.Mock<{}>).mockReturnValueOnce(true); + sut.onDidChangeActiveTextEditor(editor); - expect(sut.triggerUpdateActiveEditor).toBeCalledWith(editor) - }) - }) + expect(sut.triggerUpdateActiveEditor).toBeCalledWith(editor); + }); + }); describe('onDidChangeTextDocument()', () => { - let sut + let sut; const event: any = { document: { isDirty: false, uri: { scheme: 'file' }, }, contentChanges: [], - } + }; beforeEach(() => { - const projectWorkspace = new ProjectWorkspace(null, null, null, null) + const projectWorkspace = new ProjectWorkspace(null, null, null, null); sut = new JestExt( null, workspaceFolder, @@ -347,16 +355,16 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - }) + ); + }); function expectItTakesNoAction(event) { - sut.removeCachedTestResults = jest.fn() - sut.triggerUpdateActiveEditor = jest.fn() - sut.onDidChangeTextDocument(event) + sut.removeCachedTestResults = jest.fn(); + sut.triggerUpdateActiveEditor = jest.fn(); + sut.onDidChangeTextDocument(event); - expect(sut.removeCachedTestResults).not.toBeCalledWith(event.document) - expect(sut.triggerUpdateActiveEditor).not.toBeCalled() + expect(sut.removeCachedTestResults).not.toBeCalledWith(event.document); + expect(sut.triggerUpdateActiveEditor).not.toBeCalled(); } it('should do nothing if the document has unsaved changes', () => { @@ -366,9 +374,9 @@ describe('JestExt', () => { uri: { scheme: 'file' }, }, contentChanges: [], - } - expectItTakesNoAction(event) - }) + }; + expectItTakesNoAction(event); + }); it('should do nothing if the document URI scheme is "git"', () => { const event: any = { @@ -379,9 +387,9 @@ describe('JestExt', () => { }, }, contentChanges: [], - } - expectItTakesNoAction(event) - }) + }; + expectItTakesNoAction(event); + }); it('should do nothing if the document is clean but there are changes', () => { const event = { @@ -390,27 +398,27 @@ describe('JestExt', () => { uri: { scheme: 'file' }, }, contentChanges: { length: 1 }, - } - expectItTakesNoAction(event) - }) + }; + expectItTakesNoAction(event); + }); it('should remove the cached test results if the document is clean', () => { - sut.removeCachedTestResults = jest.fn() - window.visibleTextEditors = [] - sut.onDidChangeTextDocument(event) + sut.removeCachedTestResults = jest.fn(); + window.visibleTextEditors = []; + sut.onDidChangeTextDocument(event); - expect(sut.removeCachedTestResults).toBeCalledWith(event.document) - }) + expect(sut.removeCachedTestResults).toBeCalledWith(event.document); + }); it('should update the decorations', () => { - const editor: any = { document: event.document } - sut.triggerUpdateActiveEditor = jest.fn() - window.visibleTextEditors = [editor] - sut.onDidChangeTextDocument(event) + const editor: any = { document: event.document }; + sut.triggerUpdateActiveEditor = jest.fn(); + window.visibleTextEditors = [editor]; + sut.onDidChangeTextDocument(event); - expect(sut.triggerUpdateActiveEditor).toBeCalledWith(editor) - }) - }) + expect(sut.triggerUpdateActiveEditor).toBeCalledWith(editor); + }); + }); describe('toggleCoverageOverlay()', () => { it('should toggle the coverage overlay visibility', () => { @@ -424,15 +432,15 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - sut.triggerUpdateSettings = jest.fn() - sut.toggleCoverageOverlay() + ); + sut.triggerUpdateSettings = jest.fn(); + sut.toggleCoverageOverlay(); - expect(sut.coverageOverlay.toggleVisibility).toBeCalled() - expect(sut.triggerUpdateSettings).toBeCalled() - }) + expect(sut.coverageOverlay.toggleVisibility).toBeCalled(); + expect(sut.triggerUpdateSettings).toBeCalled(); + }); it('overrides showCoverageOnLoad settings', () => { - const settings = { showCoverageOnLoad: true } as any + const settings = { showCoverageOnLoad: true } as any; const sut = new JestExt( null, workspaceFolder, @@ -443,23 +451,23 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - expect(projectWorkspace.collectCoverage).toBe(true) + ); + expect(projectWorkspace.collectCoverage).toBe(true); - sut.restartProcess = jest.fn() - sut.coverageOverlay.enabled = false - sut.toggleCoverageOverlay() + sut.restartProcess = jest.fn(); + sut.coverageOverlay.enabled = false; + sut.toggleCoverageOverlay(); - expect(projectWorkspace.collectCoverage).toBe(false) - }) - }) + expect(projectWorkspace.collectCoverage).toBe(false); + }); + }); describe('triggerUpdateActiveEditor()', () => { beforeEach(() => { - jest.resetAllMocks() - }) + jest.resetAllMocks(); + }); it('should update the coverage overlay in visible editors', () => { - const editor: any = {} + const editor: any = {}; const sut = new JestExt( null, @@ -471,11 +479,11 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - sut.triggerUpdateActiveEditor(editor) + ); + sut.triggerUpdateActiveEditor(editor); - expect(sut.coverageOverlay.updateVisibleEditors).toBeCalled() - }) + expect(sut.coverageOverlay.updateVisibleEditors).toBeCalled(); + }); it('should update both decorators and diagnostics for valid editor', () => { const sut = new JestExt( null, @@ -487,36 +495,36 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - sut.updateDecorators = jest.fn() + ); + sut.updateDecorators = jest.fn(); const mockEditor: any = { document: { uri: { fsPath: 'file://a/b/c.ts' } }, - } - ;((sut.testResultProvider.getSortedResults as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + }; + ((sut.testResultProvider.getSortedResults as unknown) as jest.Mock<{}>).mockReturnValueOnce({ success: [], fail: [], skip: [], unknown: [], - }) - sut.triggerUpdateActiveEditor(mockEditor) + }); + sut.triggerUpdateActiveEditor(mockEditor); - expect(sut.updateDecorators).toBeCalled() - expect(updateCurrentDiagnostics).toBeCalled() - }) - }) + expect(sut.updateDecorators).toBeCalled(); + expect(updateCurrentDiagnostics).toBeCalled(); + }); + }); describe('canUpdateActiveEditor', () => { const mockTextEditor = (ext: string): any => { - const extension = ext.length ? `.${ext}` : '' + const extension = ext.length ? `.${ext}` : ''; return { document: { uri: { fsPath: `file://a/b/c${extension}` } }, - } - } + }; + }; - let sut + let sut; beforeEach(() => { - jest.resetAllMocks() - const projectWorkspace = new ProjectWorkspace(null, null, null, null) + jest.resetAllMocks(); + const projectWorkspace = new ProjectWorkspace(null, null, null, null); sut = new JestExt( null, workspaceFolder, @@ -527,52 +535,52 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - }) + ); + }); it('will skip if there is no document in editor', () => { - const editor: any = {} - expect(sut.canUpdateActiveEditor(editor)).toBe(false) - }) + const editor: any = {}; + expect(sut.canUpdateActiveEditor(editor)).toBe(false); + }); it('can not update if file is being parsed', () => { - expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(true) - sut.parsingTestFile = true - expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(false) - }) + expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(true); + sut.parsingTestFile = true; + expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(false); + }); it('can only update if document is a typescript or javascript file', () => { - expect(sut.canUpdateActiveEditor(mockTextEditor('json'))).toBe(false) - expect(sut.canUpdateActiveEditor(mockTextEditor(''))).toBe(false) - - expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(true) - expect(sut.canUpdateActiveEditor(mockTextEditor('jsx'))).toBe(true) - expect(sut.canUpdateActiveEditor(mockTextEditor('ts'))).toBe(true) - expect(sut.canUpdateActiveEditor(mockTextEditor('tsx'))).toBe(true) - }) - }) + expect(sut.canUpdateActiveEditor(mockTextEditor('json'))).toBe(false); + expect(sut.canUpdateActiveEditor(mockTextEditor(''))).toBe(false); + + expect(sut.canUpdateActiveEditor(mockTextEditor('js'))).toBe(true); + expect(sut.canUpdateActiveEditor(mockTextEditor('jsx'))).toBe(true); + expect(sut.canUpdateActiveEditor(mockTextEditor('ts'))).toBe(true); + expect(sut.canUpdateActiveEditor(mockTextEditor('tsx'))).toBe(true); + }); + }); describe('updateDecorators', () => { - let sut: JestExt - const mockEditor: any = { document: { uri: { fsPath: `file://a/b/c.js` } } } - const emptyTestResults = { success: [], fail: [], skip: [], unknown: [] } + let sut: JestExt; + const mockEditor: any = { document: { uri: { fsPath: `file://a/b/c.js` } } }; + const emptyTestResults = { success: [], fail: [], skip: [], unknown: [] }; const settings: any = { debugCodeLens: {}, enableInlineErrorMessages: false, - } + }; const tr1 = { start: { line: 1, column: 0 }, - } + }; const tr2 = { start: { line: 100, column: 0 }, - } + }; beforeEach(() => { - jest.resetAllMocks() - ;((decorations.failingItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'fail' }) - ;((decorations.passingItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'pass' }) - ;((decorations.skipItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'skip' }) - ;((decorations.notRanItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'notRan' }) + jest.resetAllMocks(); + ((decorations.failingItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'fail' }); + ((decorations.passingItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'pass' }); + ((decorations.skipItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'skip' }); + ((decorations.notRanItName as unknown) as jest.Mock<{}>).mockReturnValue({ key: 'notRan' }); - const projectWorkspace = new ProjectWorkspace(null, null, null, null) + const projectWorkspace = new ProjectWorkspace(null, null, null, null); sut = new JestExt( null, workspaceFolder, @@ -583,67 +591,69 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) + ); - mockEditor.setDecorations = jest.fn() - sut.debugCodeLensProvider.didChange = jest.fn() - }) + mockEditor.setDecorations = jest.fn(); + sut.debugCodeLensProvider.didChange = jest.fn(); + }); it('will reset decorator if testResults is empty', () => { - sut.updateDecorators(emptyTestResults, mockEditor) - expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4) + sut.updateDecorators(emptyTestResults, mockEditor); + expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4); for (const args of mockEditor.setDecorations.mock.calls) { - expect(args[1].length).toBe(0) + expect(args[1].length).toBe(0); } - }) + }); it('will generate dot dectorations for test results', () => { - const testResults2: any = { success: [tr1], fail: [tr2], skip: [], unknown: [] } - sut.updateDecorators(testResults2, mockEditor) - expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4) + const testResults2: any = { success: [tr1], fail: [tr2], skip: [], unknown: [] }; + sut.updateDecorators(testResults2, mockEditor); + expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4); for (const args of mockEditor.setDecorations.mock.calls) { switch (args[0].key) { case 'fail': case 'pass': - expect(args[1].length).toBe(1) - break + expect(args[1].length).toBe(1); + break; case 'skip': case 'notRan': - expect(args[1].length).toBe(0) - break + expect(args[1].length).toBe(0); + break; default: - expect(args[0].key).toBe('never be here') + expect(args[0].key).toBe('never be here'); } } - }) + }); it('will update inlineError decorator only if setting is enabled', () => { - const testResults2: any = { success: [], fail: [tr1, tr2], skip: [], unknown: [] } - const expected = {} - ;((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValueOnce(expected) - sut.updateDecorators(testResults2, mockEditor) - expect(decorations.failingAssertionStyle).not.toBeCalled() - expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4) - - jest.clearAllMocks() - settings.enableInlineErrorMessages = true - sut.updateDecorators(testResults2, mockEditor) - expect(decorations.failingAssertionStyle).toHaveBeenCalledTimes(2) - expect(mockEditor.setDecorations).toHaveBeenCalledTimes(6) - }) - }) + const testResults2: any = { success: [], fail: [tr1, tr2], skip: [], unknown: [] }; + const expected = {}; + ((decorations.failingAssertionStyle as unknown) as jest.Mock<{}>).mockReturnValueOnce( + expected + ); + sut.updateDecorators(testResults2, mockEditor); + expect(decorations.failingAssertionStyle).not.toBeCalled(); + expect(mockEditor.setDecorations).toHaveBeenCalledTimes(4); + + jest.clearAllMocks(); + settings.enableInlineErrorMessages = true; + sut.updateDecorators(testResults2, mockEditor); + expect(decorations.failingAssertionStyle).toHaveBeenCalledTimes(2); + expect(mockEditor.setDecorations).toHaveBeenCalledTimes(6); + }); + }); describe('detectedSnapshotErrors()', () => { - let sut: JestExt - const mockEditor: any = { document: { uri: { fsPath: `file://a/b/c.js` } } } + let sut: JestExt; + const mockEditor: any = { document: { uri: { fsPath: `file://a/b/c.js` } } }; const settings: any = { debugCodeLens: {}, enableSnapshotUpdateMessages: true, - } + }; beforeEach(() => { - jest.resetAllMocks() - const projectWorkspace = new ProjectWorkspace(null, null, null, null) + jest.resetAllMocks(); + const projectWorkspace = new ProjectWorkspace(null, null, null, null); sut = new JestExt( null, workspaceFolder, @@ -654,34 +664,34 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) + ); - mockEditor.setDecorations = jest.fn() - sut.debugCodeLensProvider.didChange = jest.fn() - }) + mockEditor.setDecorations = jest.fn(); + sut.debugCodeLensProvider.didChange = jest.fn(); + }); it('will trigger snapshot update message when a snapshot test fails', () => { - window.showInformationMessage = jest.fn(async () => null) - const spy = jest.spyOn(sut as any, 'detectedSnapshotErrors') - ;(sut as any).handleStdErr(new Error('Snapshot test failed')) - ;(sut as any).handleStdErr(new Error('Snapshot failed')) - ;(sut as any).handleStdErr(new Error('Snapshots failed')) - ;(sut as any).handleStdErr(new Error('Failed for some other reason')) - expect(spy).toHaveBeenCalledTimes(3) - }) - }) + window.showInformationMessage = jest.fn(async () => null); + const spy = jest.spyOn(sut as any, 'detectedSnapshotErrors'); + (sut as any).handleStdErr(new Error('Snapshot test failed')); + (sut as any).handleStdErr(new Error('Snapshot failed')); + (sut as any).handleStdErr(new Error('Snapshots failed')); + (sut as any).handleStdErr(new Error('Failed for some other reason')); + expect(spy).toHaveBeenCalledTimes(3); + }); + }); describe('startProcess', () => { - const projectWorkspace = new ProjectWorkspace(null, null, null, null) + const projectWorkspace = new ProjectWorkspace(null, null, null, null); const mockJestProcess = () => { - const mockProcess: any = new JestProcess({ projectWorkspace }) - mockProcess.onJestEditorSupportEvent.mockReturnValue(mockProcess) - return mockProcess - } + const mockProcess: any = new JestProcess({ projectWorkspace }); + mockProcess.onJestEditorSupportEvent.mockReturnValue(mockProcess); + return mockProcess; + }; const createJestExt = (settings: any, instanceSettings = { multirootEnv: false }) => { - ;(JestProcessManager as jest.Mock).mockClear() - const mockProcess: any = mockJestProcess() + (JestProcessManager as jest.Mock).mockClear(); + const mockProcess: any = mockJestProcess(); const jestExt = new JestExt( null, @@ -693,73 +703,79 @@ describe('JestExt', () => { debugConfigurationProvider, null, instanceSettings - ) - const mockProcessManager: any = (JestProcessManager as jest.Mock).mock.instances[0] - mockProcessManager.startJestProcess.mockReturnValue(mockProcess) - return [jestExt, mockProcessManager] - } + ); + const mockProcessManager: any = (JestProcessManager as jest.Mock).mock.instances[0]; + mockProcessManager.startJestProcess.mockReturnValue(mockProcess); + return [jestExt, mockProcessManager]; + }; it('if process already running, do nothing', () => { - const [sut, mockProcessManager] = createJestExt(extensionSettings) - mockProcessManager.numberOfProcesses = 1 - sut.startProcess() - expect(mockProcessManager.startJestProcess).not.toHaveBeenCalled() - }) + const [sut, mockProcessManager] = createJestExt(extensionSettings); + mockProcessManager.numberOfProcesses = 1; + sut.startProcess(); + expect(mockProcessManager.startJestProcess).not.toHaveBeenCalled(); + }); it('can start all tests first if configured.', () => { - const [sut, mockProcessManager] = createJestExt({ ...extensionSettings, runAllTestsFirst: true }) + const [sut, mockProcessManager] = createJestExt({ + ...extensionSettings, + runAllTestsFirst: true, + }); - const { runAllTestsFirstInWatchMode } = (JestProcessManager as jest.Mock).mock.calls[0][0] - expect(runAllTestsFirstInWatchMode).toBeTruthy() + const { runAllTestsFirstInWatchMode } = (JestProcessManager as jest.Mock).mock.calls[0][0]; + expect(runAllTestsFirstInWatchMode).toBeTruthy(); - sut.startProcess() - expect(mockProcessManager.startJestProcess).toHaveBeenCalled() - }) + sut.startProcess(); + expect(mockProcessManager.startJestProcess).toHaveBeenCalled(); + }); it('can start watch mode first if configured.', () => { - const [sut, mockProcessManager] = createJestExt({ ...extensionSettings, runAllTestsFirst: false }) + const [sut, mockProcessManager] = createJestExt({ + ...extensionSettings, + runAllTestsFirst: false, + }); - const { runAllTestsFirstInWatchMode } = (JestProcessManager as jest.Mock).mock.calls[0][0] - expect(runAllTestsFirstInWatchMode).toBeFalsy() + const { runAllTestsFirstInWatchMode } = (JestProcessManager as jest.Mock).mock.calls[0][0]; + expect(runAllTestsFirstInWatchMode).toBeFalsy(); - sut.startProcess() - expect(mockProcessManager.startJestProcess).toHaveBeenCalled() - }) + sut.startProcess(); + expect(mockProcessManager.startJestProcess).toHaveBeenCalled(); + }); describe('exitCallback', () => { - const [sut, mockProcessManager] = createJestExt(extensionSettings, { multirootEnv: true }) - sut.startProcess() - const { exitCallback } = mockProcessManager.startJestProcess.mock.calls[0][0] + const [sut, mockProcessManager] = createJestExt(extensionSettings, { multirootEnv: true }); + sut.startProcess(); + const { exitCallback } = mockProcessManager.startJestProcess.mock.calls[0][0]; it('if receive watchMode process: prepare and report for the next run', () => { - const p1: any = mockJestProcess() - const p2: any = mockJestProcess() + const p1: any = mockJestProcess(); + const p2: any = mockJestProcess(); - exitCallback(p1, p2) + exitCallback(p1, p2); - expect(p1.onJestEditorSupportEvent).not.toHaveBeenCalled() - expect(p2.onJestEditorSupportEvent).toHaveBeenCalled() - }) + expect(p1.onJestEditorSupportEvent).not.toHaveBeenCalled(); + expect(p2.onJestEditorSupportEvent).toHaveBeenCalled(); + }); it('if process ends unexpectedly, report error', () => { - const p1: any = mockJestProcess() - p1.stopRequested.mockReturnValue(false) - exitCallback(p1) - expect(p1.onJestEditorSupportEvent).not.toHaveBeenCalled() - expect(messaging.systemErrorMessage).toHaveBeenCalled() - const msg: string = (messaging.systemErrorMessage as jest.Mock).mock.calls[0][0] - expect(msg.includes(workspaceFolder.name)).toBeTruthy() - }) - }) - }) + const p1: any = mockJestProcess(); + p1.stopRequested.mockReturnValue(false); + exitCallback(p1); + expect(p1.onJestEditorSupportEvent).not.toHaveBeenCalled(); + expect(messaging.systemErrorMessage).toHaveBeenCalled(); + const msg: string = (messaging.systemErrorMessage as jest.Mock).mock.calls[0][0]; + expect(msg.includes(workspaceFolder.name)).toBeTruthy(); + }); + }); + }); describe('handleJestEditorSupportEvent()', () => { - let sut: JestExt + let sut: JestExt; const settings: any = { debugCodeLens: {}, enableSnapshotUpdateMessages: true, - } + }; beforeEach(() => { - jest.resetAllMocks() - const projectWorkspace = new ProjectWorkspace(null, null, null, null) + jest.resetAllMocks(); + const projectWorkspace = new ProjectWorkspace(null, null, null, null); sut = new JestExt( null, workspaceFolder, @@ -770,28 +786,28 @@ describe('JestExt', () => { debugConfigurationProvider, null, null - ) - }) + ); + }); it('will change status according to received output', () => { - update.mockClear() - ;(sut as any).handleJestEditorSupportEvent('onRunStart') - expect(update).toHaveBeenCalledTimes(1) - expect(update).toHaveBeenCalledWith('running', 'Running tests', []) - update.mockClear() - ;(sut as any).handleJestEditorSupportEvent('onRunComplete') - expect(update).toHaveBeenCalledTimes(1) - expect(update).toHaveBeenCalledWith('stopped', undefined, []) - }) + update.mockClear(); + (sut as any).handleJestEditorSupportEvent('onRunStart'); + expect(update).toHaveBeenCalledTimes(1); + expect(update).toHaveBeenCalledWith('running', 'Running tests', []); + update.mockClear(); + (sut as any).handleJestEditorSupportEvent('onRunComplete'); + expect(update).toHaveBeenCalledTimes(1); + expect(update).toHaveBeenCalledWith('stopped', undefined, []); + }); it('will append line to output', () => { - channelStub.appendLine.mockClear() - ;(sut as any).handleJestEditorSupportEvent('not ignored output') - expect(channelStub.appendLine).toHaveBeenCalledTimes(1) - expect(channelStub.appendLine).toHaveBeenCalledWith('not ignored output') - channelStub.appendLine.mockClear() - ;(sut as any).handleJestEditorSupportEvent('onRunComplete') - expect(channelStub.appendLine).not.toHaveBeenCalled() - }) - }) -}) + channelStub.appendLine.mockClear(); + (sut as any).handleJestEditorSupportEvent('not ignored output'); + expect(channelStub.appendLine).toHaveBeenCalledTimes(1); + expect(channelStub.appendLine).toHaveBeenCalledWith('not ignored output'); + channelStub.appendLine.mockClear(); + (sut as any).handleJestEditorSupportEvent('onRunComplete'); + expect(channelStub.appendLine).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/tests/JestProcessManagement/JestProcess.test.ts b/tests/JestProcessManagement/JestProcess.test.ts index 8f669a22e..d72e92cc3 100644 --- a/tests/JestProcessManagement/JestProcess.test.ts +++ b/tests/JestProcessManagement/JestProcess.test.ts @@ -1,501 +1,501 @@ -jest.unmock('../../src/JestProcessManagement/JestProcess') - -import { Runner, ProjectWorkspace } from 'jest-editor-support' -import { JestProcess } from '../../src/JestProcessManagement/JestProcess' -import { EventEmitter } from 'events' -import { WatchMode } from '../../src/Jest' -import { normalize } from 'path' -jest.unmock('path') +jest.unmock('../../src/JestProcessManagement/JestProcess'); + +import { Runner, ProjectWorkspace } from 'jest-editor-support'; +import { JestProcess } from '../../src/JestProcessManagement/JestProcess'; +import { EventEmitter } from 'events'; +import { WatchMode } from '../../src/Jest'; +import { normalize } from 'path'; +jest.unmock('path'); jest.mock('vscode', () => ({ extensions: { getExtension: () => { - return { extensionPath: normalize('/my/vscode/extensions') } + return { extensionPath: normalize('/my/vscode/extensions') }; }, }, -})) +})); describe('JestProcess', () => { - let projectWorkspaceMock - let jestProcess - const runnerMock = (Runner as any) as jest.Mock<any> - let runnerMockImplementation - let eventEmitter + let projectWorkspaceMock; + let jestProcess; + const runnerMock = (Runner as any) as jest.Mock<any>; + let runnerMockImplementation; + let eventEmitter; beforeEach(() => { - jest.clearAllMocks() - projectWorkspaceMock = new ProjectWorkspace(null, null, null, null) + jest.clearAllMocks(); + projectWorkspaceMock = new ProjectWorkspace(null, null, null, null); runnerMockImplementation = { on: jest.fn(() => this), start: jest.fn(), runJestWithUpdateForSnapshots: jest.fn(), - } - }) + }; + }); describe('Runner', () => { it('loads reporter from path', () => { - new JestProcess(projectWorkspaceMock) + new JestProcess(projectWorkspaceMock); expect(runnerMock).toHaveBeenCalledWith(undefined, { noColor: true, reporters: ['default', `"${normalize('/my/vscode/extensions/out/reporter.js')}"`], - }) - }) - }) + }); + }); + }); describe('when creating', () => { beforeEach(() => { - runnerMock.mockImplementation(() => runnerMockImplementation) - }) + runnerMock.mockImplementation(() => runnerMockImplementation); + }); it('accepts a project workspace argument', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - }) - expect(jestProcess).not.toBe(null) - }) + }); + expect(jestProcess).not.toBe(null); + }); it('clears the stopRequested flag', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - }) - expect(jestProcess.stopRequested()).toBeFalsy() - }) + }); + expect(jestProcess.stopRequested()).toBeFalsy(); + }); it('records watchMode in the watchMode property', () => { - const expected = WatchMode.Watch + const expected = WatchMode.Watch; jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, watchMode: expected, - }) + }); - expect(jestProcess.watchMode).toBe(expected) - }) + expect(jestProcess.watchMode).toBe(expected); + }); it('creates an instance of jest-editor-support runner', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - }) - expect(runnerMock.mock.instances.length).toBe(1) - }) + }); + expect(runnerMock.mock.instances.length).toBe(1); + }); it('passes the workspace argument to the jest-editor-support Runner', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - }) - expect(runnerMock.mock.calls[0][0]).toBe(projectWorkspaceMock) - }) + }); + expect(runnerMock.mock.calls[0][0]).toBe(projectWorkspaceMock); + }); it('starts the jest-editor-support Runner without watch mode by default', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - }) - expect(runnerMockImplementation.start).toHaveBeenCalledTimes(1) - expect(runnerMockImplementation.start.mock.calls[0]).toEqual([false, false]) - }) + }); + expect(runnerMockImplementation.start).toHaveBeenCalledTimes(1); + expect(runnerMockImplementation.start.mock.calls[0]).toEqual([false, false]); + }); it('starts the jest-editor-support Runner in --watch mode', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, watchMode: WatchMode.Watch, - }) - expect(runnerMockImplementation.start.mock.calls[0]).toEqual([true, false]) - }) + }); + expect(runnerMockImplementation.start.mock.calls[0]).toEqual([true, false]); + }); it('starts the jest-editor-support Runner in --watchAll mode', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, watchMode: WatchMode.WatchAll, - }) - expect(runnerMockImplementation.start.mock.calls[0]).toEqual([true, true]) - }) - }) + }); + expect(runnerMockImplementation.start.mock.calls[0]).toEqual([true, true]); + }); + }); describe('when jest-editor-support runner exits', () => { - let onExit + let onExit; beforeEach(() => { - eventEmitter = new EventEmitter() + eventEmitter = new EventEmitter(); runnerMockImplementation = { ...runnerMockImplementation, on: (event, callback) => { - eventEmitter.on(event, callback) + eventEmitter.on(event, callback); }, - } - runnerMock.mockImplementation(() => runnerMockImplementation) + }; + runnerMock.mockImplementation(() => runnerMockImplementation); jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - }) - onExit = jest.fn() - jestProcess.onExit(onExit) - }) + }); + onExit = jest.fn(); + jestProcess.onExit(onExit); + }); it('calls the callback provided to onExit', () => { - eventEmitter.emit('debuggerProcessExit') + eventEmitter.emit('debuggerProcessExit'); - expect(onExit).toHaveBeenCalledTimes(1) - }) + expect(onExit).toHaveBeenCalledTimes(1); + }); it('does not set the stopRequested flag', () => { - eventEmitter.emit('debuggerProcessExit') + eventEmitter.emit('debuggerProcessExit'); - expect(jestProcess.stopRequested()).toBeFalsy() - }) + expect(jestProcess.stopRequested()).toBeFalsy(); + }); it('calls the callback with the argument being the instance of the jest process', () => { - eventEmitter.emit('debuggerProcessExit') + eventEmitter.emit('debuggerProcessExit'); - expect(onExit.mock.calls[0][0]).toBe(jestProcess) - }) + expect(onExit.mock.calls[0][0]).toBe(jestProcess); + }); it('only responds to first debuggerProcessExit event from the runner', () => { - eventEmitter.emit('debuggerProcessExit') - eventEmitter.emit('debuggerProcessExit') + eventEmitter.emit('debuggerProcessExit'); + eventEmitter.emit('debuggerProcessExit'); - expect(onExit).toHaveBeenCalledTimes(1) - }) - }) + expect(onExit).toHaveBeenCalledTimes(1); + }); + }); describe('when subscribing to regular jest-editor-support events', () => { - let eventHandler - const jestEditorSupportedEvent = 'jest-editor-supported-event' + let eventHandler; + const jestEditorSupportedEvent = 'jest-editor-supported-event'; beforeEach(() => { - eventEmitter = new EventEmitter() + eventEmitter = new EventEmitter(); runnerMockImplementation = { ...runnerMockImplementation, on: (event, callback) => { - eventEmitter.on(event, callback) + eventEmitter.on(event, callback); }, - } - runnerMock.mockImplementation(() => runnerMockImplementation) + }; + runnerMock.mockImplementation(() => runnerMockImplementation); jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - }) - eventHandler = jest.fn() - }) + }); + eventHandler = jest.fn(); + }); it('simply forwards the events to event emitter', () => { - jestProcess.onJestEditorSupportEvent(jestEditorSupportedEvent, eventHandler) - eventEmitter.emit(jestEditorSupportedEvent) + jestProcess.onJestEditorSupportEvent(jestEditorSupportedEvent, eventHandler); + eventEmitter.emit(jestEditorSupportedEvent); - expect(eventHandler).toHaveBeenCalledTimes(1) - }) + expect(eventHandler).toHaveBeenCalledTimes(1); + }); it('forwards any argument to the provider event handler', () => { - jestProcess.onJestEditorSupportEvent(jestEditorSupportedEvent, eventHandler) - eventEmitter.emit(jestEditorSupportedEvent, 'arg1', { value: 'arg2' }) + jestProcess.onJestEditorSupportEvent(jestEditorSupportedEvent, eventHandler); + eventEmitter.emit(jestEditorSupportedEvent, 'arg1', { value: 'arg2' }); - expect(eventHandler).toHaveBeenCalledTimes(1) - expect(eventHandler.mock.calls[0][0]).toBe('arg1') - expect(eventHandler.mock.calls[0][1]).toEqual({ value: 'arg2' }) - }) - }) + expect(eventHandler).toHaveBeenCalledTimes(1); + expect(eventHandler.mock.calls[0][0]).toBe('arg1'); + expect(eventHandler.mock.calls[0][1]).toEqual({ value: 'arg2' }); + }); + }); describe('when stopping', () => { - const closeProcessMock = jest.fn() + const closeProcessMock = jest.fn(); beforeEach(() => { - eventEmitter = new EventEmitter() + eventEmitter = new EventEmitter(); runnerMockImplementation = { ...runnerMockImplementation, on: (event, callback) => { - eventEmitter.on(event, callback) + eventEmitter.on(event, callback); }, closeProcess: closeProcessMock, - } - runnerMock.mockImplementation(() => runnerMockImplementation) + }; + runnerMock.mockImplementation(() => runnerMockImplementation); jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - }) - jest.useFakeTimers() - }) + }); + jest.useFakeTimers(); + }); it('calls closeProcess on the underlying runner from jest-editor-support', () => { - jestProcess.stop() + jestProcess.stop(); - expect(closeProcessMock).toHaveBeenCalledTimes(1) - }) + expect(closeProcessMock).toHaveBeenCalledTimes(1); + }); it('sets the stopRequested flag', () => { - jestProcess.stop() + jestProcess.stop(); - expect(jestProcess.stopRequested()).toBeTruthy() - }) + expect(jestProcess.stopRequested()).toBeTruthy(); + }); it('resolves promise when recieving debuggerProcessExit event', async () => { - expect.assertions(1) - const promise = jestProcess.stop() - eventEmitter.emit('debuggerProcessExit') - await expect(promise).resolves.toBeUndefined() - }) + expect.assertions(1); + const promise = jestProcess.stop(); + eventEmitter.emit('debuggerProcessExit'); + await expect(promise).resolves.toBeUndefined(); + }); it('resolves promise by timeout', async () => { - expect.assertions(1) - const promise = jestProcess.stop() - jest.runAllTimers() - await expect(promise).resolves.toBeUndefined() - }) + expect.assertions(1); + const promise = jestProcess.stop(); + jest.runAllTimers(); + await expect(promise).resolves.toBeUndefined(); + }); it('do not hangs on multiple stop() calls', async () => { - expect.assertions(1) - const promise = jestProcess.stop() - jestProcess.stop() - jest.runAllTimers() - await expect(promise).resolves.toBeUndefined() - }) - }) + expect.assertions(1); + const promise = jestProcess.stop(); + jestProcess.stop(); + jest.runAllTimers(); + await expect(promise).resolves.toBeUndefined(); + }); + }); describe('when process is created with keepAlive set to true', () => { - let onExit + let onExit; beforeEach(() => { - eventEmitter = new EventEmitter() + eventEmitter = new EventEmitter(); runnerMockImplementation = { ...runnerMockImplementation, on: (event, callback) => { - eventEmitter.on(event, callback) + eventEmitter.on(event, callback); }, removeAllListeners: jest.fn(() => eventEmitter.removeAllListeners()), closeProcess: jest.fn(), - } - runnerMock.mockImplementation(() => runnerMockImplementation) - onExit = jest.fn() - }) + }; + runnerMock.mockImplementation(() => runnerMockImplementation); + onExit = jest.fn(); + }); it('provides public attribute providing the keepAlive setting', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) + }); - expect(jestProcess.keepAlive).toBeTruthy() + expect(jestProcess.keepAlive).toBeTruthy(); jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: false, - }) + }); - expect(jestProcess.keepAlive).toBeFalsy() - }) + expect(jestProcess.keepAlive).toBeFalsy(); + }); it('creates new instance of jest-editor-support Runner', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) - jestProcess.onExit(onExit) - eventEmitter.emit('debuggerProcessExit') - expect(runnerMock.mock.instances.length).toBe(2) - }) + }); + jestProcess.onExit(onExit); + eventEmitter.emit('debuggerProcessExit'); + expect(runnerMock.mock.instances.length).toBe(2); + }); it('limits number of restarts (keepAlive boundary)', () => { - const limit = JestProcess.keepAliveLimit + const limit = JestProcess.keepAliveLimit; jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) - jestProcess.onExit(onExit) + }); + jestProcess.onExit(onExit); Array.from(Array(limit + 1).keys()).forEach((_) => { - eventEmitter.emit('debuggerProcessExit') - }) - expect(runnerMock.mock.instances.length).toBe(limit) - }) + eventEmitter.emit('debuggerProcessExit'); + }); + expect(runnerMock.mock.instances.length).toBe(limit); + }); it('does not restart the process if stopped explicitely', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) - jestProcess.onExit(onExit) - eventEmitter.emit('debuggerProcessExit') - eventEmitter.emit('debuggerProcessExit') - jestProcess.stop() - eventEmitter.emit('debuggerProcessExit') - expect(runnerMock.mock.instances.length).toBe(3) - }) + }); + jestProcess.onExit(onExit); + eventEmitter.emit('debuggerProcessExit'); + eventEmitter.emit('debuggerProcessExit'); + jestProcess.stop(); + eventEmitter.emit('debuggerProcessExit'); + expect(runnerMock.mock.instances.length).toBe(3); + }); it('passes the workspace argument to the jest-editor-support Runner', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) - jestProcess.onExit(onExit) - eventEmitter.emit('debuggerProcessExit') - expect(runnerMock.mock.calls[1][0]).toBe(projectWorkspaceMock) - }) + }); + jestProcess.onExit(onExit); + eventEmitter.emit('debuggerProcessExit'); + expect(runnerMock.mock.calls[1][0]).toBe(projectWorkspaceMock); + }); it('starts the jest-editor-support runner', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) - jestProcess.onExit(onExit) - eventEmitter.emit('debuggerProcessExit') - expect(runnerMockImplementation.start).toHaveBeenCalledTimes(2) - }) + }); + jestProcess.onExit(onExit); + eventEmitter.emit('debuggerProcessExit'); + expect(runnerMockImplementation.start).toHaveBeenCalledTimes(2); + }); it('passes the watchMode argument to the new runner instance when it is true', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, watchMode: WatchMode.WatchAll, keepAlive: true, - }) - jestProcess.onExit(onExit) - eventEmitter.emit('debuggerProcessExit') + }); + jestProcess.onExit(onExit); + eventEmitter.emit('debuggerProcessExit'); - expect(runnerMockImplementation.start.mock.calls[1]).toEqual([true, true]) - }) + expect(runnerMockImplementation.start.mock.calls[1]).toEqual([true, true]); + }); it('removes all event listeners from the previous instance of the runner', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) - jestProcess.onExit(onExit) - eventEmitter.emit('debuggerProcessExit') + }); + jestProcess.onExit(onExit); + eventEmitter.emit('debuggerProcessExit'); - expect(runnerMockImplementation.removeAllListeners).toHaveBeenCalledTimes(1) - }) + expect(runnerMockImplementation.removeAllListeners).toHaveBeenCalledTimes(1); + }); it('does not call the exit callback if number of restart attempts did not reach JestProcess.keepAliveLimit', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) - jestProcess.onExit(onExit) + }); + jestProcess.onExit(onExit); for (let i = 0; i < JestProcess.keepAliveLimit - 1; i++) { - eventEmitter.emit('debuggerProcessExit') + eventEmitter.emit('debuggerProcessExit'); } - expect(onExit).not.toHaveBeenCalled() - }) + expect(onExit).not.toHaveBeenCalled(); + }); it('call the exit callback if number of restart attempts is equal or greater than JestProcess.keepAliveLimit', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) - jestProcess.onExit(onExit) + }); + jestProcess.onExit(onExit); for (let i = 0; i < JestProcess.keepAliveLimit; i++) { - eventEmitter.emit('debuggerProcessExit') + eventEmitter.emit('debuggerProcessExit'); } - expect(onExit).toHaveBeenCalledTimes(1) - }) - }) + expect(onExit).toHaveBeenCalledTimes(1); + }); + }); describe('restoring jest events', () => { - let jestEvents + let jestEvents; beforeEach(() => { - eventEmitter = new EventEmitter() + eventEmitter = new EventEmitter(); jestEvents = new Map< string, { - callback: (...args: any[]) => void - count: number + callback: (...args: any[]) => void; + count: number; } - >() + >(); runnerMockImplementation = { ...runnerMockImplementation, on: (event, callback) => { if (event === 'debuggerProcessExit') { - eventEmitter.on(event, callback) + eventEmitter.on(event, callback); } - let count = 0 + let count = 0; if (jestEvents.has(event)) { - count = jestEvents.get(event).count + count = jestEvents.get(event).count; } jestEvents.set(event, { callback, count: count + 1, - }) + }); }, removeAllListeners: jest.fn(() => eventEmitter.removeAllListeners()), closeProcess: jest.fn(), - } - runnerMock.mockImplementation(() => runnerMockImplementation) - }) + }; + runnerMock.mockImplementation(() => runnerMockImplementation); + }); it('restores the previously registered jest event', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) + }); - const handler = () => jestProcess + const handler = () => jestProcess; - jestProcess.onJestEditorSupportEvent('event', handler) - eventEmitter.emit('debuggerProcessExit') + jestProcess.onJestEditorSupportEvent('event', handler); + eventEmitter.emit('debuggerProcessExit'); - expect(jestEvents.has('event')).toBeTruthy() - expect(jestEvents.get('event').count).toBe(2) - expect(jestEvents.get('event').callback).toBe(handler) - }) + expect(jestEvents.has('event')).toBeTruthy(); + expect(jestEvents.get('event').count).toBe(2); + expect(jestEvents.get('event').callback).toBe(handler); + }); it('restores the previously registered multiple jest events', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) + }); - const handler1 = () => jestProcess - const handler2 = () => jestProcess + const handler1 = () => jestProcess; + const handler2 = () => jestProcess; - jestProcess.onJestEditorSupportEvent('event1', handler1) - jestProcess.onJestEditorSupportEvent('event2', handler2) - eventEmitter.emit('debuggerProcessExit') + jestProcess.onJestEditorSupportEvent('event1', handler1); + jestProcess.onJestEditorSupportEvent('event2', handler2); + eventEmitter.emit('debuggerProcessExit'); - expect(jestEvents.has('event1')).toBeTruthy() - expect(jestEvents.get('event1').count).toBe(2) - expect(jestEvents.get('event1').callback).toBe(handler1) + expect(jestEvents.has('event1')).toBeTruthy(); + expect(jestEvents.get('event1').count).toBe(2); + expect(jestEvents.get('event1').callback).toBe(handler1); - expect(jestEvents.has('event2')).toBeTruthy() - expect(jestEvents.get('event2').count).toBe(2) - expect(jestEvents.get('event2').callback).toBe(handler2) - }) + expect(jestEvents.has('event2')).toBeTruthy(); + expect(jestEvents.get('event2').count).toBe(2); + expect(jestEvents.get('event2').callback).toBe(handler2); + }); it('does not restore the previously registered jest event if keepAlive is false', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: false, - }) + }); - const handler = () => jestProcess + const handler = () => jestProcess; - jestProcess.onJestEditorSupportEvent('event', handler) - eventEmitter.emit('debuggerProcessExit') + jestProcess.onJestEditorSupportEvent('event', handler); + eventEmitter.emit('debuggerProcessExit'); - expect(jestEvents.get('event').count).toBe(1) - }) + expect(jestEvents.get('event').count).toBe(1); + }); it('does not restore any events when explicitely stopped', () => { jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, keepAlive: true, - }) + }); - const handler = () => jestProcess + const handler = () => jestProcess; - jestProcess.onJestEditorSupportEvent('event', handler) - jestProcess.stop() - eventEmitter.emit('debuggerProcessExit') + jestProcess.onJestEditorSupportEvent('event', handler); + jestProcess.stop(); + eventEmitter.emit('debuggerProcessExit'); - expect(jestEvents.get('event').count).toBe(1) - }) - }) + expect(jestEvents.get('event').count).toBe(1); + }); + }); describe('updating snapshots', () => { beforeEach(() => { - runnerMock.mockImplementation(() => runnerMockImplementation) + runnerMock.mockImplementation(() => runnerMockImplementation); jestProcess = new JestProcess({ projectWorkspace: projectWorkspaceMock, - }) - }) + }); + }); it('calls runJestWithUpdateForSnapshots on the underlying runner from jest-editor-support', () => { - const callback = () => {} - jestProcess.runJestWithUpdateForSnapshots(callback) - expect(runnerMockImplementation.runJestWithUpdateForSnapshots).toHaveBeenCalledWith(callback) - }) - }) -}) + const callback = () => {}; + jestProcess.runJestWithUpdateForSnapshots(callback); + expect(runnerMockImplementation.runJestWithUpdateForSnapshots).toHaveBeenCalledWith(callback); + }); + }); +}); diff --git a/tests/JestProcessManagement/JestProcessManager.test.ts b/tests/JestProcessManagement/JestProcessManager.test.ts index f8d2edd5e..5d081bd7a 100644 --- a/tests/JestProcessManagement/JestProcessManager.test.ts +++ b/tests/JestProcessManagement/JestProcessManager.test.ts @@ -1,115 +1,118 @@ -jest.unmock('../../src/JestProcessManagement/JestProcessManager') +jest.unmock('../../src/JestProcessManagement/JestProcessManager'); -import { ProjectWorkspace } from 'jest-editor-support' -import { JestProcessManager } from '../../src/JestProcessManagement/JestProcessManager' -import { JestProcess } from '../../src/JestProcessManagement/JestProcess' -import { EventEmitter } from 'events' -import { WatchMode } from '../../src/Jest' +import { ProjectWorkspace } from 'jest-editor-support'; +import { JestProcessManager } from '../../src/JestProcessManagement/JestProcessManager'; +import { JestProcess } from '../../src/JestProcessManagement/JestProcess'; +import { EventEmitter } from 'events'; +import { WatchMode } from '../../src/Jest'; describe('JestProcessManager', () => { - let jestProcessManager - let projectWorkspaceMock - let exitHandler - let eventEmitter + let jestProcessManager; + let projectWorkspaceMock; + let exitHandler; + let eventEmitter; - const jestProcessMock = (JestProcess as any) as jest.Mock<any> + const jestProcessMock = (JestProcess as any) as jest.Mock<any>; beforeEach(() => { - jest.clearAllMocks() - projectWorkspaceMock = new ProjectWorkspace(null, null, null, null) - jestProcessManager = new JestProcessManager({ projectWorkspace: projectWorkspaceMock }) - exitHandler = jest.fn() - eventEmitter = new EventEmitter() - }) + jest.clearAllMocks(); + projectWorkspaceMock = new ProjectWorkspace(null, null, null, null); + jestProcessManager = new JestProcessManager({ projectWorkspace: projectWorkspaceMock }); + exitHandler = jest.fn(); + eventEmitter = new EventEmitter(); + }); describe('when creating', () => { it('accepts Project Workspace as the argument', () => { // tslint:disable-next-line no-shadowed-variable - const jestProcessManager = new JestProcessManager({ projectWorkspace: projectWorkspaceMock }) - expect(jestProcessManager).not.toBe(null) - }) + const jestProcessManager = new JestProcessManager({ projectWorkspace: projectWorkspaceMock }); + expect(jestProcessManager).not.toBe(null); + }); it('accepts runAllTestsFirstInWatchMode argument (true if not provided)', () => { // tslint:disable-next-line no-shadowed-variable const jestProcessManager = new JestProcessManager({ projectWorkspace: projectWorkspaceMock, runAllTestsFirstInWatchMode: false, - }) - expect(jestProcessManager).not.toBe(null) - }) - }) + }); + expect(jestProcessManager).not.toBe(null); + }); + }); describe('when starting jest process', () => { it('creates JestProcess', () => { - jestProcessManager.startJestProcess() + jestProcessManager.startJestProcess(); - expect(jestProcessMock).toHaveBeenCalledTimes(1) - }) + expect(jestProcessMock).toHaveBeenCalledTimes(1); + }); it('returns an instance of JestProcess', () => { - const jestProcess = jestProcessManager.startJestProcess() + const jestProcess = jestProcessManager.startJestProcess(); - expect(jestProcess).toBe(jestProcessMock.mock.instances[0]) - }) + expect(jestProcess).toBe(jestProcessMock.mock.instances[0]); + }); it('passes the project workspace to the JestProcess instance', () => { - jestProcessManager.startJestProcess() + jestProcessManager.startJestProcess(); - expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('projectWorkspace', projectWorkspaceMock) - }) + expect(jestProcessMock.mock.calls[0][0]).toHaveProperty( + 'projectWorkspace', + projectWorkspaceMock + ); + }); it('calls the onExit handler when JestProcess exits', () => { const mockImplementation = { keepAlive: false, onExit: (callback) => { - eventEmitter.on('debuggerProcessExit', callback) + eventEmitter.on('debuggerProcessExit', callback); }, - } - jestProcessMock.mockImplementation(() => mockImplementation) + }; + jestProcessMock.mockImplementation(() => mockImplementation); - jestProcessManager.startJestProcess({ exitCallback: exitHandler }) + jestProcessManager.startJestProcess({ exitCallback: exitHandler }); - eventEmitter.emit('debuggerProcessExit', mockImplementation) + eventEmitter.emit('debuggerProcessExit', mockImplementation); - expect(exitHandler).toHaveBeenCalledTimes(1) - }) - }) + expect(exitHandler).toHaveBeenCalledTimes(1); + }); + }); describe('when starting jest process in non-watch mode', () => { it('passes the watchMode flag set to false', () => { - jestProcessManager.startJestProcess() + jestProcessManager.startJestProcess(); - expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('watchMode', WatchMode.None) - }) - }) + expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('watchMode', WatchMode.None); + }); + }); describe('when starting jest process in keep-alive mode', () => { it('passes the keepAlive flag set to true', () => { - jestProcessManager.startJestProcess({ keepAlive: true }) + jestProcessManager.startJestProcess({ keepAlive: true }); - expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('keepAlive', true) - }) - }) + expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('keepAlive', true); + }); + }); describe('when starting jest process in non keep-alive mode', () => { it('passes the keepAlive flag set to false', () => { - jestProcessManager.startJestProcess({ keepAlive: false }) + jestProcessManager.startJestProcess({ keepAlive: false }); - expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('keepAlive', false) - }) + expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('keepAlive', false); + }); it('passes the keepAlive flag set to false when no flag is specified', () => { - jestProcessManager.startJestProcess() + jestProcessManager.startJestProcess(); - expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('keepAlive', false) - }) - }) + expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('keepAlive', false); + }); + }); describe('when starting jest process in watch mode', () => { it('will run all tests without watch mode then restart with --watch', () => { - jestProcessManager.startJestProcess({ watchMode: WatchMode.Watch }) + jestProcessManager.startJestProcess({ watchMode: WatchMode.Watch }); - eventEmitter.emit('debuggerProcessExit', { stopRequested: () => false }) + eventEmitter.emit('debuggerProcessExit', { stopRequested: () => false }); expect(jestProcessMock.mock.calls).toEqual([ [ @@ -126,121 +129,130 @@ describe('JestProcessManager', () => { watchMode: WatchMode.Watch, }, ], - ]) - }) + ]); + }); it('starts the process for non-watch mode with keep-alive flag set to false', () => { jestProcessManager.startJestProcess({ keepAlive: true, watchMode: WatchMode.Watch, - }) + }); // we need this to trigger the watch-mode process that only starts // after the non-watch-mode process exits - eventEmitter.emit('debuggerProcessExit', { stopRequested: () => false }) + eventEmitter.emit('debuggerProcessExit', { stopRequested: () => false }); - expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('keepAlive', false) - expect(jestProcessMock.mock.calls[1][0]).toHaveProperty('keepAlive', true) - }) + expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('keepAlive', false); + expect(jestProcessMock.mock.calls[1][0]).toHaveProperty('keepAlive', true); + }); it('starts both jest processes with the same project workspace', () => { - jestProcessManager.startJestProcess({ watchMode: WatchMode.Watch }) + jestProcessManager.startJestProcess({ watchMode: WatchMode.Watch }); - eventEmitter.emit('debuggerProcessExit', { stopRequested: () => false }) + eventEmitter.emit('debuggerProcessExit', { stopRequested: () => false }); - expect(jestProcessMock.mock.calls[0][0]).toHaveProperty('projectWorkspace', projectWorkspaceMock) - expect(jestProcessMock.mock.calls[1][0]).toHaveProperty('projectWorkspace', projectWorkspaceMock) - }) + expect(jestProcessMock.mock.calls[0][0]).toHaveProperty( + 'projectWorkspace', + projectWorkspaceMock + ); + expect(jestProcessMock.mock.calls[1][0]).toHaveProperty( + 'projectWorkspace', + projectWorkspaceMock + ); + }); it('binds the provided exit handler to the both jest processes', () => { - const eventEmitterForWatchMode = new EventEmitter() + const eventEmitterForWatchMode = new EventEmitter(); const onExitMock = jest .fn() .mockImplementationOnce((callback) => { - eventEmitter.on('debuggerProcessExit', callback) + eventEmitter.on('debuggerProcessExit', callback); }) .mockImplementationOnce((callback) => { - eventEmitterForWatchMode.on('debuggerProcessExit', callback) - }) + eventEmitterForWatchMode.on('debuggerProcessExit', callback); + }); jestProcessMock.mockImplementation(() => ({ onExit: onExitMock, - })) + })); jestProcessManager.startJestProcess({ exitCallback: exitHandler, watchMode: WatchMode.Watch, - }) + }); - eventEmitter.emit('debuggerProcessExit', { stopRequested: () => false, watchMode: false }) - eventEmitterForWatchMode.emit('debuggerProcessExit', { stopRequested: () => false, watchMode: true }) + eventEmitter.emit('debuggerProcessExit', { stopRequested: () => false, watchMode: false }); + eventEmitterForWatchMode.emit('debuggerProcessExit', { + stopRequested: () => false, + watchMode: true, + }); - expect(exitHandler).toHaveBeenCalledTimes(2) - expect(exitHandler.mock.calls[0][0].watchMode).toBe(false) - expect(exitHandler.mock.calls[1][0].watchMode).toBe(true) - }) + expect(exitHandler).toHaveBeenCalledTimes(2); + expect(exitHandler.mock.calls[0][0].watchMode).toBe(false); + expect(exitHandler.mock.calls[1][0].watchMode).toBe(true); + }); it('the exit handler for the non-watch mode passes the jest process representing the watch mode as the second argument', () => { - const eventEmitterForWatchMode = new EventEmitter() + const eventEmitterForWatchMode = new EventEmitter(); const onExitMock = jest .fn() .mockImplementationOnce((callback) => { - eventEmitter.on('debuggerProcessExit', callback) + eventEmitter.on('debuggerProcessExit', callback); }) .mockImplementationOnce((callback) => { - eventEmitterForWatchMode.on('debuggerProcessExit', callback) - }) + eventEmitterForWatchMode.on('debuggerProcessExit', callback); + }); const mockImplementation = { onExit: onExitMock, restart: jest.fn(), stopRequested: () => false, - } + }; - jestProcessMock.mockImplementation(() => mockImplementation) + jestProcessMock.mockImplementation(() => mockImplementation); jestProcessManager.startJestProcess({ exitCallback: exitHandler, watchMode: WatchMode.Watch, - }) + }); - eventEmitter.emit('debuggerProcessExit', mockImplementation) - eventEmitterForWatchMode.emit('debuggerProcessExit', mockImplementation) + eventEmitter.emit('debuggerProcessExit', mockImplementation); + eventEmitterForWatchMode.emit('debuggerProcessExit', mockImplementation); - expect(exitHandler.mock.calls[0].length).toBe(2) - expect(exitHandler.mock.calls[0][1]).toBe(mockImplementation) - }) - }) + expect(exitHandler.mock.calls[0].length).toBe(2); + expect(exitHandler.mock.calls[0][1]).toBe(mockImplementation); + }); + }); describe('when stopping jest process', () => { it('stops the most recent running jest process', () => { - const stopMock = jest.fn() + const stopMock = jest.fn(); jestProcessMock.mockImplementation(() => ({ onExit: jest.fn(), stop: stopMock, - })) - const jestProcess = jestProcessManager.startJestProcess() + })); + const jestProcess = jestProcessManager.startJestProcess(); - jestProcessManager.stopJestProcess(jestProcess) + jestProcessManager.stopJestProcess(jestProcess); - expect(stopMock).toHaveBeenCalledTimes(1) - expect(jestProcessManager.numberOfProcesses).toBe(0) - }) + expect(stopMock).toHaveBeenCalledTimes(1); + expect(jestProcessManager.numberOfProcesses).toBe(0); + }); it('stopJestProcess can stop the process even if it is not in the process list', () => { - const stopMock = jest.fn() + const stopMock = jest.fn(); jestProcessMock.mockImplementation(() => ({ onExit: jest.fn(), stop: stopMock, - })) - const jestProcess = jestProcessManager.startJestProcess() - const cloned = { ...jestProcess } + })); + const jestProcess = jestProcessManager.startJestProcess(); + const cloned = { ...jestProcess }; - expect(jestProcessManager.numberOfProcesses).toBe(1) - jestProcessManager.stopJestProcess(cloned) + expect(jestProcessManager.numberOfProcesses).toBe(1); + jestProcessManager.stopJestProcess(cloned); - expect(stopMock).toHaveBeenCalledTimes(1) - expect(jestProcessManager.numberOfProcesses).toBe(1) - }) + expect(stopMock).toHaveBeenCalledTimes(1); + expect(jestProcessManager.numberOfProcesses).toBe(1); + }); // jest mocking does not let us test it properly because // jestProcessMock.instances does not work as expected @@ -248,169 +260,169 @@ describe('JestProcessManager', () => { const mockImplementation = { onExit: jest.fn(), stop: jest.fn(), - } + }; - jestProcessMock.mockImplementation(() => mockImplementation) + jestProcessMock.mockImplementation(() => mockImplementation); - jestProcessManager.startJestProcess() - jestProcessManager.startJestProcess() + jestProcessManager.startJestProcess(); + jestProcessManager.startJestProcess(); - const stopAll = jestProcessManager.stopAll() + const stopAll = jestProcessManager.stopAll(); - expect(stopAll).toBeInstanceOf(Promise) + expect(stopAll).toBeInstanceOf(Promise); return stopAll.then(() => { - expect(jestProcessMock).toHaveBeenCalledTimes(2) - expect(mockImplementation.stop).toHaveBeenCalledTimes(2) - expect(jestProcessManager.numberOfProcesses).toBe(0) - }) - }) + expect(jestProcessMock).toHaveBeenCalledTimes(2); + expect(mockImplementation.stop).toHaveBeenCalledTimes(2); + expect(jestProcessManager.numberOfProcesses).toBe(0); + }); + }); describe('2-staged process: runAllTest => runWatchMode', () => { - let startOptions = {} + let startOptions = {}; beforeEach(() => { jestProcessMock.mockImplementation(() => { - let isStopped = false - const stop = () => (isStopped = true) - const stopRequested = () => isStopped + let isStopped = false; + const stop = () => (isStopped = true); + const stopRequested = () => isStopped; const onExit = (callback) => { - eventEmitter.on('debuggerProcessExit', callback) - } - return { onExit, stop, stopRequested } - }) + eventEmitter.on('debuggerProcessExit', callback); + }; + return { onExit, stop, stopRequested }; + }); startOptions = { exitCallback: exitHandler, watchMode: WatchMode.Watch, - } - }) + }; + }); it('normally the watch-mode process will auto-start when first process exit', () => { - const p = jestProcessManager.startJestProcess(startOptions) - expect(jestProcessMock).toHaveBeenCalledTimes(1) + const p = jestProcessManager.startJestProcess(startOptions); + expect(jestProcessMock).toHaveBeenCalledTimes(1); - eventEmitter.emit('debuggerProcessExit', p) + eventEmitter.emit('debuggerProcessExit', p); - expect(jestProcessMock).toHaveBeenCalledTimes(2) - }) + expect(jestProcessMock).toHaveBeenCalledTimes(2); + }); it('stopAll will prevent auto start the watch mode process', async () => { - const p = jestProcessManager.startJestProcess(startOptions) - expect(jestProcessMock).toHaveBeenCalledTimes(1) - expect(jestProcessManager.numberOfProcesses).toBe(1) + const p = jestProcessManager.startJestProcess(startOptions); + expect(jestProcessMock).toHaveBeenCalledTimes(1); + expect(jestProcessManager.numberOfProcesses).toBe(1); - await jestProcessManager.stopAll() - expect(jestProcessManager.numberOfProcesses).toBe(0) + await jestProcessManager.stopAll(); + expect(jestProcessManager.numberOfProcesses).toBe(0); - eventEmitter.emit('debuggerProcessExit', p) - expect(jestProcessManager.numberOfProcesses).toBe(0) - expect(jestProcessMock).toHaveBeenCalledTimes(1) - }) + eventEmitter.emit('debuggerProcessExit', p); + expect(jestProcessManager.numberOfProcesses).toBe(0); + expect(jestProcessMock).toHaveBeenCalledTimes(1); + }); it('stopJestProcess will prevent auto start the watch mode process', async () => { - const p = jestProcessManager.startJestProcess(startOptions) - expect(jestProcessMock).toHaveBeenCalledTimes(1) - expect(jestProcessManager.numberOfProcesses).toBe(1) + const p = jestProcessManager.startJestProcess(startOptions); + expect(jestProcessMock).toHaveBeenCalledTimes(1); + expect(jestProcessManager.numberOfProcesses).toBe(1); - await jestProcessManager.stopJestProcess(p) - expect(jestProcessManager.numberOfProcesses).toBe(0) + await jestProcessManager.stopJestProcess(p); + expect(jestProcessManager.numberOfProcesses).toBe(0); - eventEmitter.emit('debuggerProcessExit', p) - expect(jestProcessManager.numberOfProcesses).toBe(0) - expect(jestProcessMock).toHaveBeenCalledTimes(1) - }) - }) + eventEmitter.emit('debuggerProcessExit', p); + expect(jestProcessManager.numberOfProcesses).toBe(0); + expect(jestProcessMock).toHaveBeenCalledTimes(1); + }); + }); it('does not stop any jest process if none is running', () => { const mockImplementation = { onExit: jest.fn(), stop: jest.fn(), - } + }; - jestProcessMock.mockImplementation(() => mockImplementation) + jestProcessMock.mockImplementation(() => mockImplementation); - const stopAll = jestProcessManager.stopAll() + const stopAll = jestProcessManager.stopAll(); - expect(stopAll).toBeInstanceOf(Promise) + expect(stopAll).toBeInstanceOf(Promise); return stopAll.then(() => { - expect(jestProcessMock).toHaveBeenCalledTimes(0) - expect(mockImplementation.stop).not.toHaveBeenCalled() - expect(jestProcessManager.numberOfProcesses).toBe(0) - }) - }) - }) + expect(jestProcessMock).toHaveBeenCalledTimes(0); + expect(mockImplementation.stop).not.toHaveBeenCalled(); + expect(jestProcessManager.numberOfProcesses).toBe(0); + }); + }); + }); describe('jest process exits with keepAlive === true', () => { it('removes the reference to the jest process that has been stopped', () => { - const jestProcess = jestProcessManager.startJestProcess({ keepAlive: true }) + const jestProcess = jestProcessManager.startJestProcess({ keepAlive: true }); - jestProcessManager.stopJestProcess(jestProcess) + jestProcessManager.stopJestProcess(jestProcess); - expect(jestProcessManager.numberOfProcesses).toBe(0) - }) + expect(jestProcessManager.numberOfProcesses).toBe(0); + }); it('removes the reference to the jest process that has been stopped and the following onExit event does not do anything', () => { jestProcessMock.mockImplementation(() => ({ keepAlive: true, onExit: (callback) => { - eventEmitter.on('debuggerProcessExit', callback) + eventEmitter.on('debuggerProcessExit', callback); }, stop: jest.fn(), - })) + })); - const jestProcess = jestProcessManager.startJestProcess({ keepAlive: true }) - jestProcessManager.stopJestProcess(jestProcess) + const jestProcess = jestProcessManager.startJestProcess({ keepAlive: true }); + jestProcessManager.stopJestProcess(jestProcess); - eventEmitter.emit('debuggerProcessExit', jestProcess) + eventEmitter.emit('debuggerProcessExit', jestProcess); - expect(jestProcessManager.numberOfProcesses).toBe(0) - expect(jestProcess.stop).toHaveBeenCalledTimes(1) - }) + expect(jestProcessManager.numberOfProcesses).toBe(0); + expect(jestProcess.stop).toHaveBeenCalledTimes(1); + }); it('keeps the reference to the jest process that exited on its own but then restarted', () => { jestProcessMock.mockImplementation(() => ({ keepAlive: true, onExit: (callback) => { - eventEmitter.on('debuggerProcessExit', callback) + eventEmitter.on('debuggerProcessExit', callback); }, - })) + })); - const jestProcess = jestProcessManager.startJestProcess({ keepAlive: true }) + const jestProcess = jestProcessManager.startJestProcess({ keepAlive: true }); - eventEmitter.emit('debuggerProcessExit', jestProcess) + eventEmitter.emit('debuggerProcessExit', jestProcess); - expect(jestProcessManager.numberOfProcesses).toBe(1) - }) + expect(jestProcessManager.numberOfProcesses).toBe(1); + }); it('removes the reference to the jest process that exited on its own that preceeds the jest process for watch mode', () => { jestProcessMock.mockImplementation(() => ({ onExit: (callback) => { - eventEmitter.on('debuggerProcessExit', callback) + eventEmitter.on('debuggerProcessExit', callback); }, stopRequested: () => false, - })) + })); const jestProcess = jestProcessManager.startJestProcess({ keepAlive: true, watchMode: WatchMode.Watch, - }) + }); - eventEmitter.emit('debuggerProcessExit', jestProcess) + eventEmitter.emit('debuggerProcessExit', jestProcess); - expect(jestProcessManager.numberOfProcesses).toBe(1) - }) + expect(jestProcessManager.numberOfProcesses).toBe(1); + }); it('keeps the reference to the jest process in watch-mode that exited on its own', () => { - const eventEmitterForWatchMode = new EventEmitter() + const eventEmitterForWatchMode = new EventEmitter(); const onExitMock = jest .fn() .mockImplementationOnce((callback) => { - eventEmitter.on('debuggerProcessExit', callback) + eventEmitter.on('debuggerProcessExit', callback); }) .mockImplementationOnce((callback) => { - eventEmitterForWatchMode.on('debuggerProcessExit', callback) - }) + eventEmitterForWatchMode.on('debuggerProcessExit', callback); + }); const mockImplementation = { keepAlive: true, onExit: onExitMock, restart: jest.fn(), - } - jestProcessMock.mockImplementation(() => mockImplementation) + }; + jestProcessMock.mockImplementation(() => mockImplementation); const jestProcess = jestProcessManager.startJestProcess({ watch: true, @@ -418,88 +430,88 @@ describe('JestProcessManager', () => { exitCallback: (_, jestProcessInWatchMode) => { if (jestProcessInWatchMode) { // this one will exit the watch-mode process - eventEmitterForWatchMode.emit('debuggerProcessExit', jestProcessInWatchMode) + eventEmitterForWatchMode.emit('debuggerProcessExit', jestProcessInWatchMode); } }, - }) + }); // this one will exit the run-all-tests process - eventEmitter.emit('debuggerProcessExit', jestProcess) + eventEmitter.emit('debuggerProcessExit', jestProcess); // there should be one process left - the watch mode process is kept-alive - expect(jestProcessManager.numberOfProcesses).toBe(1) - }) - }) + expect(jestProcessManager.numberOfProcesses).toBe(1); + }); + }); describe('jest process exits with keepAlive === false', () => { it('removes the reference to the jest process that exited on its own', () => { jestProcessMock.mockImplementation(() => ({ keepAlive: false, onExit: (callback) => { - eventEmitter.on('debuggerProcessExit', callback) + eventEmitter.on('debuggerProcessExit', callback); }, restart: jest.fn(), - })) + })); - const jestProcess = jestProcessManager.startJestProcess() + const jestProcess = jestProcessManager.startJestProcess(); - eventEmitter.emit('debuggerProcessExit', jestProcess) + eventEmitter.emit('debuggerProcessExit', jestProcess); - expect(jestProcessManager.numberOfProcesses).toBe(0) - }) + expect(jestProcessManager.numberOfProcesses).toBe(0); + }); it('removes the reference to the jest process in watch-mode that exited on its own', () => { - const eventEmitterForWatchMode = new EventEmitter() + const eventEmitterForWatchMode = new EventEmitter(); const onExitMock = jest .fn() .mockImplementationOnce((callback) => { - eventEmitter.on('debuggerProcessExit', callback) + eventEmitter.on('debuggerProcessExit', callback); }) .mockImplementationOnce((callback) => { - eventEmitterForWatchMode.on('debuggerProcessExit', callback) - }) + eventEmitterForWatchMode.on('debuggerProcessExit', callback); + }); const mockImplementation = { keepAlive: false, onExit: onExitMock, restart: jest.fn(), - } - jestProcessMock.mockImplementation(() => mockImplementation) + }; + jestProcessMock.mockImplementation(() => mockImplementation); const jestProcess = jestProcessManager.startJestProcess({ watch: true, exitCallback: (_, jestProcessInWatchMode) => { if (jestProcessInWatchMode) { - eventEmitterForWatchMode.emit('debuggerProcessExit', jestProcessInWatchMode) + eventEmitterForWatchMode.emit('debuggerProcessExit', jestProcessInWatchMode); } }, - }) + }); - eventEmitter.emit('debuggerProcessExit', jestProcess) + eventEmitter.emit('debuggerProcessExit', jestProcess); - expect(jestProcessManager.numberOfProcesses).toBe(0) - }) - }) + expect(jestProcessManager.numberOfProcesses).toBe(0); + }); + }); describe('when runAllTestsFirstInWatchMode is false', () => { it('does not run all tests first', () => { jestProcessManager = new JestProcessManager({ projectWorkspace: projectWorkspaceMock, runAllTestsFirstInWatchMode: false, - }) + }); jestProcessMock.mockImplementation(() => ({ onExit: (callback) => { - eventEmitter.on('debuggerProcessExit', callback) + eventEmitter.on('debuggerProcessExit', callback); }, - })) + })); - const watchMode = WatchMode.Watch - const jestProcess = jestProcessManager.startJestProcess({ watchMode }) + const watchMode = WatchMode.Watch; + const jestProcess = jestProcessManager.startJestProcess({ watchMode }); - eventEmitter.emit('debuggerProcessExit', jestProcess) + eventEmitter.emit('debuggerProcessExit', jestProcess); - expect(jestProcessMock).toHaveBeenCalledTimes(1) + expect(jestProcessMock).toHaveBeenCalledTimes(1); expect(jestProcessMock.mock.calls[0]).toEqual([ { @@ -507,7 +519,7 @@ describe('JestProcessManager', () => { projectWorkspace: projectWorkspaceMock, watchMode, }, - ]) - }) - }) -}) + ]); + }); + }); +}); diff --git a/tests/Settings/index.test.ts b/tests/Settings/index.test.ts index b61fe96c5..0d737d8a9 100644 --- a/tests/Settings/index.test.ts +++ b/tests/Settings/index.test.ts @@ -1,16 +1,16 @@ -jest.unmock('../../src/Settings') -import { isDefaultPathToJest } from '../../src/Settings' +jest.unmock('../../src/Settings'); +import { isDefaultPathToJest } from '../../src/Settings'; describe('isDefaultPathToJest', () => { it('returns true when the value is null', () => { - expect(isDefaultPathToJest(null)).toBe(true) - }) + expect(isDefaultPathToJest(null)).toBe(true); + }); it('returns true for the legacy default ""', () => { - expect(isDefaultPathToJest('')).toBe(true) - }) + expect(isDefaultPathToJest('')).toBe(true); + }); it('returns false otherwise', () => { - expect(isDefaultPathToJest('something')).toBe(false) - }) -}) + expect(isDefaultPathToJest('something')).toBe(false); + }); +}); diff --git a/tests/StatusBar.test.ts b/tests/StatusBar.test.ts index e5f4879cb..b521b5cb9 100644 --- a/tests/StatusBar.test.ts +++ b/tests/StatusBar.test.ts @@ -1,5 +1,5 @@ -jest.unmock('../src/StatusBar') -jest.useFakeTimers() +jest.unmock('../src/StatusBar'); +jest.useFakeTimers(); /* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "assertRender"] }] */ const newStatusBarItem = () => ({ @@ -8,228 +8,236 @@ const newStatusBarItem = () => ({ show: jest.fn(), hide: jest.fn(), tooltip: '', -}) +}); -const statusBarItem = newStatusBarItem() +const statusBarItem = newStatusBarItem(); -import * as vscode from 'vscode' -import { StatusBar, StatusType, Status } from '../src/StatusBar' +import * as vscode from 'vscode'; +import { StatusBar, StatusType, Status } from '../src/StatusBar'; -const createStatusBarItem = jest.fn().mockReturnValue(statusBarItem) +const createStatusBarItem = jest.fn().mockReturnValue(statusBarItem); -const mockedChannel = { append: () => {}, clear: () => {} } as any -vscode.window.createOutputChannel = jest.fn(() => mockedChannel) -;((vscode.window.createStatusBarItem as unknown) as jest.Mock<{}>) = createStatusBarItem +const mockedChannel = { append: () => {}, clear: () => {} } as any; +vscode.window.createOutputChannel = jest.fn(() => mockedChannel); +((vscode.window.createStatusBarItem as unknown) as jest.Mock<{}>) = createStatusBarItem; describe('StatusBar', () => { - let statusBar: StatusBar - const updateStatusSpy = jest.spyOn(StatusBar.prototype as any, 'updateStatus') - const renderSpy = jest.spyOn(StatusBar.prototype as any, 'render') + let statusBar: StatusBar; + const updateStatusSpy = jest.spyOn(StatusBar.prototype as any, 'updateStatus'); + const renderSpy = jest.spyOn(StatusBar.prototype as any, 'render'); beforeEach(() => { - statusBar = new StatusBar() - updateStatusSpy.mockClear() - renderSpy.mockClear() - statusBarItem.text = '' - }) + statusBar = new StatusBar(); + updateStatusSpy.mockClear(); + renderSpy.mockClear(); + statusBarItem.text = ''; + }); const assertRender = (nth: number, request: any, type: StatusType) => { - const n = nth < 0 ? renderSpy.mock.calls.length - 1 : nth - const args = renderSpy.mock.calls[n] + const n = nth < 0 ? renderSpy.mock.calls.length - 1 : nth; + const args = renderSpy.mock.calls[n]; - expect(args[0]).toEqual(request) - expect((args[1] as any).type).toEqual(type) - } + expect(args[0]).toEqual(request); + expect((args[1] as any).type).toEqual(type); + }; describe('register', () => { it('should add 2 commands', () => { - statusBar.register(() => undefined) - expect(vscode.commands.registerCommand).toBeCalledTimes(2) + statusBar.register(() => undefined); + expect(vscode.commands.registerCommand).toBeCalledTimes(2); - const registerCommand = (vscode.commands.registerCommand as unknown) as jest.Mock<{}> - const calls = registerCommand.mock.calls - expect(calls.some((c) => c[0].includes('show-summary-output'))).toBe(true) - expect(calls.some((c) => c[0].includes('show-active-output'))).toBe(true) - }) - }) + const registerCommand = (vscode.commands.registerCommand as unknown) as jest.Mock<{}>; + const calls = registerCommand.mock.calls; + expect(calls.some((c) => c[0].includes('show-summary-output'))).toBe(true); + expect(calls.some((c) => c[0].includes('show-active-output'))).toBe(true); + }); + }); describe('bind()', () => { it('returns binded helpers', () => { - const source = 'testSource' - const helpers = statusBar.bind(source) - ;['initial', 'running', 'success', 'failed', 'stopped'].forEach((status) => { - helpers.update(status as Status) - expect(updateStatusSpy).toHaveBeenCalledWith({ source, status }) - }) - }) - }) + const source = 'testSource'; + const helpers = statusBar.bind(source); + ['initial', 'running', 'success', 'failed', 'stopped'].forEach((status) => { + helpers.update(status as Status); + expect(updateStatusSpy).toHaveBeenCalledWith({ source, status }); + }); + }); + }); describe('request()', () => { it('should update status', () => { - statusBar.bind('testSource1').update('initial') + statusBar.bind('testSource1').update('initial'); - expect(updateStatusSpy).toHaveBeenCalled() - }) - }) + expect(updateStatusSpy).toHaveBeenCalled(); + }); + }); describe('updateStatus()', () => { it('should pick most relevant status', () => { // first instance failed, display it as folder status - statusBar.bind('testSource1').update('failed') - assertRender(0, { source: 'testSource1', status: 'failed' }, StatusType.active) + statusBar.bind('testSource1').update('failed'); + assertRender(0, { source: 'testSource1', status: 'failed' }, StatusType.active); // then second is running, this status is more important then previous, will display as workspace status - statusBar.bind('testSource2').update('running') - assertRender(1, { source: 'testSource2', status: 'running' }, StatusType.summary) + statusBar.bind('testSource2').update('running'); + assertRender(1, { source: 'testSource2', status: 'running' }, StatusType.summary); // second is ok, display first instance fail as it is more important, will display as workspace status - statusBar.bind('testSource2').update('success') - assertRender(2, { source: 'testSource1', status: 'failed' }, StatusType.summary) - }) + statusBar.bind('testSource2').update('success'); + assertRender(2, { source: 'testSource1', status: 'failed' }, StatusType.summary); + }); it('can display modes', () => { // first instance failed, display it as folder status - statusBar.bind('testSource1').update('failed', 'some reason', ['watch', 'coverage']) - expect(statusBarItem.text).toEqual('Jest: $(alert) some reason $(eye) $(color-mode)') + statusBar.bind('testSource1').update('failed', 'some reason', ['watch', 'coverage']); + expect(statusBarItem.text).toEqual('Jest: $(alert) some reason $(eye) $(color-mode)'); - statusBar.bind('testSource1').update('success', undefined, ['watch']) - expect(statusBarItem.text).toEqual('Jest: $(check) $(eye)') - }) - }) + statusBar.bind('testSource1').update('success', undefined, ['watch']); + expect(statusBarItem.text).toEqual('Jest: $(check) $(eye)'); + }); + }); describe('multiroot status', () => { - const statusBarItem2 = newStatusBarItem() + const statusBarItem2 = newStatusBarItem(); const editor: any = { document: { uri: 'whatever' }, - } + }; const getStatusBarItems = () => { if (statusBarItem.tooltip.includes('summary')) { - return { active: statusBarItem2, summary: statusBarItem } + return { active: statusBarItem2, summary: statusBarItem }; } - return { active: statusBarItem, summary: statusBarItem2 } - } - const getWorkspaceFolder = jest.fn() - ;((vscode.workspace.getWorkspaceFolder as unknown) as jest.Mock<{}>) = getWorkspaceFolder + return { active: statusBarItem, summary: statusBarItem2 }; + }; + const getWorkspaceFolder = jest.fn(); + ((vscode.workspace.getWorkspaceFolder as unknown) as jest.Mock<{}>) = getWorkspaceFolder; beforeEach(() => { - jest.clearAllMocks() - jest.clearAllTimers() - createStatusBarItem.mockReturnValueOnce(statusBarItem).mockReturnValueOnce(statusBarItem2) - statusBar = new StatusBar() - statusBarItem.text = '' - statusBarItem2.text = '' - }) + jest.clearAllMocks(); + jest.clearAllTimers(); + createStatusBarItem.mockReturnValueOnce(statusBarItem).mockReturnValueOnce(statusBarItem2); + statusBar = new StatusBar(); + statusBarItem.text = ''; + statusBarItem2.text = ''; + }); it('create 2 statusBarItem', () => { - expect(createStatusBarItem).toBeCalledTimes(2) - }) + expect(createStatusBarItem).toBeCalledTimes(2); + }); it('only update active status for single root', () => { - statusBar.bind('testSource').update('initial') - const { active, summary } = getStatusBarItems() - expect(active.text).toEqual('Jest: ...') - expect(summary.text).toEqual('') - }) + statusBar.bind('testSource').update('initial'); + const { active, summary } = getStatusBarItems(); + expect(active.text).toEqual('Jest: ...'); + expect(summary.text).toEqual(''); + }); it('update both status for multiroot', () => { - const { active, summary } = getStatusBarItems() + const { active, summary } = getStatusBarItems(); - statusBar.bind('testSource1').update('initial') - expect(active.show).toBeCalledTimes(1) - expect(active.text).toEqual('Jest: ...') + statusBar.bind('testSource1').update('initial'); + expect(active.show).toBeCalledTimes(1); + expect(active.text).toEqual('Jest: ...'); - statusBar.bind('testSource2').update('initial') - expect(summary.show).toBeCalledTimes(1) - expect(summary.text).toEqual('Jest-WS: ...') + statusBar.bind('testSource2').update('initial'); + expect(summary.show).toBeCalledTimes(1); + expect(summary.text).toEqual('Jest-WS: ...'); // without active folder, the active status will be hidden in multiroot - expect(active.hide).toBeCalledTimes(1) - }) + expect(active.hide).toBeCalledTimes(1); + }); it('can show active status from active editor', () => { - const { active } = getStatusBarItems() + const { active } = getStatusBarItems(); - statusBar.bind('testSource1').update('initial') - statusBar.bind('testSource2').update('initial') + statusBar.bind('testSource1').update('initial'); + statusBar.bind('testSource2').update('initial'); // without active folder, the active status will be hidden in multiroot - expect(active.show).toBeCalledTimes(1) - expect(active.hide).toBeCalledTimes(1) + expect(active.show).toBeCalledTimes(1); + expect(active.hide).toBeCalledTimes(1); - getWorkspaceFolder.mockReturnValue({ name: 'testSource1' }) - statusBar.onDidChangeActiveTextEditor(editor) + getWorkspaceFolder.mockReturnValue({ name: 'testSource1' }); + statusBar.onDidChangeActiveTextEditor(editor); - expect(active.show).toBeCalledTimes(2) - expect(active.hide).toBeCalledTimes(1) - }) + expect(active.show).toBeCalledTimes(2); + expect(active.hide).toBeCalledTimes(1); + }); it('can animate both running status', () => { - getWorkspaceFolder.mockReturnValue({ name: 'testSource1' }) - statusBar.onDidChangeActiveTextEditor(editor) + getWorkspaceFolder.mockReturnValue({ name: 'testSource1' }); + statusBar.onDidChangeActiveTextEditor(editor); - statusBar.bind('testSource1').update('running') - statusBar.bind('testSource2').update('running') + statusBar.bind('testSource1').update('running'); + statusBar.bind('testSource2').update('running'); - expect(renderSpy).toHaveBeenCalledTimes(2) + expect(renderSpy).toHaveBeenCalledTimes(2); - const calls: any[][] = renderSpy.mock.calls - expect(calls.every((c) => c[0].status === 'running')).toBe(true) - expect(calls.some((c) => c[1].type === StatusType.active)).toBe(true) - expect(calls.some((c) => c[1].type !== StatusType.active)).toBe(true) - }) + const calls: any[][] = renderSpy.mock.calls; + expect(calls.every((c) => c[0].status === 'running')).toBe(true); + expect(calls.some((c) => c[1].type === StatusType.active)).toBe(true); + expect(calls.some((c) => c[1].type !== StatusType.active)).toBe(true); + }); it('when hiding status, spinner should be stopped too', () => { - const { active, summary } = getStatusBarItems() + const { active, summary } = getStatusBarItems(); // sending 2 request without activeFolder should disable the active status - statusBar.bind('testSource1').update('running') - expect(active.show).toBeCalledTimes(1) - expect(summary.show).toBeCalledTimes(0) + statusBar.bind('testSource1').update('running'); + expect(active.show).toBeCalledTimes(1); + expect(summary.show).toBeCalledTimes(0); - jest.clearAllMocks() - jest.clearAllTimers() + jest.clearAllMocks(); + jest.clearAllTimers(); - statusBar.bind('testSource2').update('initial') - expect(active.show).toBeCalledTimes(0) - expect(summary.show).toBeCalledTimes(1) + statusBar.bind('testSource2').update('initial'); + expect(active.show).toBeCalledTimes(0); + expect(summary.show).toBeCalledTimes(1); - expect(active.hide).toBeCalledTimes(1) - }) - }) + expect(active.hide).toBeCalledTimes(1); + }); + }); describe('StatusBarItem', () => { - const registerCommand = (vscode.commands.registerCommand as unknown) as jest.Mock<{}> + const registerCommand = (vscode.commands.registerCommand as unknown) as jest.Mock<{}>; beforeEach(() => { // reset statusBar to clear its internal `activeFolder` - statusBar = new StatusBar() - registerCommand.mockReset() - }) + statusBar = new StatusBar(); + registerCommand.mockReset(); + }); afterEach(() => { - ;(vscode.workspace as any).workspaceFolders = [] - vscode.window.activeTextEditor = undefined - }) + (vscode.workspace as any).workspaceFolders = []; + vscode.window.activeTextEditor = undefined; + }); it('responds to clicks for one active WorkspaceFolder', () => { - const getExtensionByName = jest.fn() - statusBar.register(getExtensionByName) - - const statusBarClickHandler = registerCommand.mock.calls.find((c) => c[0].includes('show-active-output'))[1] - expect(statusBarClickHandler).toBeDefined() - ;(vscode.workspace as any).workspaceFolders = [{ name: 'testproject', uri: vscode.Uri.file(''), index: 0 }] - statusBarClickHandler() - expect(getExtensionByName).toBeCalledWith('testproject') - }) + const getExtensionByName = jest.fn(); + statusBar.register(getExtensionByName); + + const statusBarClickHandler = registerCommand.mock.calls.find((c) => + c[0].includes('show-active-output') + )[1]; + expect(statusBarClickHandler).toBeDefined(); + (vscode.workspace as any).workspaceFolders = [ + { name: 'testproject', uri: vscode.Uri.file(''), index: 0 }, + ]; + statusBarClickHandler(); + expect(getExtensionByName).toBeCalledWith('testproject'); + }); it('responds to clicks for two WorkspaceFolders but only one active', () => { - const getExtensionByName = jest.fn() - statusBar.register(getExtensionByName) - - const statusBarClickHandler = registerCommand.mock.calls.find((c) => c[0].includes('show-active-output'))[1] - expect(statusBarClickHandler).toBeDefined() - ;(vscode.workspace as any).workspaceFolders = [ + const getExtensionByName = jest.fn(); + statusBar.register(getExtensionByName); + + const statusBarClickHandler = registerCommand.mock.calls.find((c) => + c[0].includes('show-active-output') + )[1]; + expect(statusBarClickHandler).toBeDefined(); + (vscode.workspace as any).workspaceFolders = [ { name: 'testproject1', uri: vscode.Uri.file(''), index: 0 }, { name: 'testproject2', uri: vscode.Uri.file(''), index: 1 }, { name: 'testproject3', uri: vscode.Uri.file(''), index: 2 }, - ] - const projectUrl = vscode.Uri.file('projecturl') - vscode.window.activeTextEditor = ({ document: { uri: projectUrl } } as unknown) as vscode.TextEditor + ]; + const projectUrl = vscode.Uri.file('projecturl'); + vscode.window.activeTextEditor = ({ + document: { uri: projectUrl }, + } as unknown) as vscode.TextEditor; vscode.workspace.getWorkspaceFolder = (url) => - url === projectUrl ? vscode.workspace.workspaceFolders[1] : undefined - statusBarClickHandler() - expect(getExtensionByName).toBeCalledWith(vscode.workspace.workspaceFolders[1].name) - }) - }) -}) + url === projectUrl ? vscode.workspace.workspaceFolders[1] : undefined; + statusBarClickHandler(); + expect(getExtensionByName).toBeCalledWith(vscode.workspace.workspaceFolders[1].name); + }); + }); +}); diff --git a/tests/TestResults/TestResult.test.ts b/tests/TestResults/TestResult.test.ts index 2c09efb83..dfe257e2a 100644 --- a/tests/TestResults/TestResult.test.ts +++ b/tests/TestResults/TestResult.test.ts @@ -1,9 +1,9 @@ -jest.unmock('../../src/TestResults/TestResult') -jest.unmock('../../src/helpers') -jest.mock('path', () => ({ sep: require.requireActual('path').sep })) +jest.unmock('../../src/TestResults/TestResult'); +jest.unmock('../../src/helpers'); +jest.mock('path', () => ({ sep: require.requireActual('path').sep })); -import * as TestResult from '../../src/TestResults/TestResult' -import * as path from 'path' +import * as TestResult from '../../src/TestResults/TestResult'; +import * as path from 'path'; const { resultsWithLowerCaseWindowsDriveLetters, @@ -11,47 +11,47 @@ const { testResultsWithLowerCaseWindowsDriveLetters, resultsWithoutAnsiEscapeSequence, withLowerCaseWindowsDriveLetter, -} = TestResult +} = TestResult; describe('TestResult', () => { describe('resultsWithLowerCaseWindowsDriveLetters', () => { describe('on POSIX systems', () => { it('should return the results unchanged', () => { - ;(path as any).sep = '/' - const expected: any = {} + (path as any).sep = '/'; + const expected: any = {}; - expect(resultsWithLowerCaseWindowsDriveLetters(expected)).toBe(expected) - }) - }) + expect(resultsWithLowerCaseWindowsDriveLetters(expected)).toBe(expected); + }); + }); // tslint:disable no-shadowed-variable describe('on Windows systems', () => { beforeEach(() => { - ;(path as any).sep = '\\' - }) + (path as any).sep = '\\'; + }); it('should normalize paths in the coverage map', () => { - const spy = jest.spyOn(TestResult, 'coverageMapWithLowerCaseWindowsDriveLetters') - const data: any = { coverageMap: {} } - resultsWithLowerCaseWindowsDriveLetters(data) - expect(spy).toHaveBeenCalled() - }) + const spy = jest.spyOn(TestResult, 'coverageMapWithLowerCaseWindowsDriveLetters'); + const data: any = { coverageMap: {} }; + resultsWithLowerCaseWindowsDriveLetters(data); + expect(spy).toHaveBeenCalled(); + }); it('should normalize paths in the test results', () => { - const spy = jest.spyOn(TestResult, 'testResultsWithLowerCaseWindowsDriveLetters') - const data: any = { coverageMap: {} } - resultsWithLowerCaseWindowsDriveLetters(data) - expect(spy).toHaveBeenCalled() - }) - }) - }) + const spy = jest.spyOn(TestResult, 'testResultsWithLowerCaseWindowsDriveLetters'); + const data: any = { coverageMap: {} }; + resultsWithLowerCaseWindowsDriveLetters(data); + expect(spy).toHaveBeenCalled(); + }); + }); + }); describe('resultsWithoutAnsiEscapeSequence', () => { it('should not break when there is no data or testResults', () => { - expect(resultsWithoutAnsiEscapeSequence(undefined)).toEqual(undefined) - const noTestResults: any = {} - expect(resultsWithoutAnsiEscapeSequence(noTestResults)).toEqual({}) - }) + expect(resultsWithoutAnsiEscapeSequence(undefined)).toEqual(undefined); + const noTestResults: any = {}; + expect(resultsWithoutAnsiEscapeSequence(noTestResults)).toEqual({}); + }); it('should remove ANSI characters from the test results when provided', () => { const data: any = { testResults: [ @@ -67,43 +67,50 @@ describe('TestResult', () => { ], }, ], - } + }; expect(resultsWithoutAnsiEscapeSequence(data).testResults).toEqual([ { message: '<body> <div> <div class="root" > Learn React </div> </div></body>', assertionResults: [ - { failureMessages: ['<body> <div> <div class="root" > Learn React </div> </div></body>'] }, + { + failureMessages: [ + '<body> <div> <div class="root" > Learn React </div> </div></body>', + ], + }, ], }, - ]) - }) - }) + ]); + }); + }); describe('testResultsWithLowerCaseWindowsDriveLetters', () => { it('should return nothing the test results when the results are undefined', () => { - const testResults: any = undefined - expect(testResultsWithLowerCaseWindowsDriveLetters(testResults)).toBeUndefined() - }) + const testResults: any = undefined; + expect(testResultsWithLowerCaseWindowsDriveLetters(testResults)).toBeUndefined(); + }); it('should return the test results when no tests were run', () => { - const testResults: any = [] - expect(testResultsWithLowerCaseWindowsDriveLetters(testResults)).toEqual(testResults) - }) + const testResults: any = []; + expect(testResultsWithLowerCaseWindowsDriveLetters(testResults)).toEqual(testResults); + }); it('should normalizes paths in the test results when provided', () => { - const testResults: any = [{ name: 'c:\\drive\\is\\lowercase' }, { name: 'D:\\drive\\is\\uppercase' }] + const testResults: any = [ + { name: 'c:\\drive\\is\\lowercase' }, + { name: 'D:\\drive\\is\\uppercase' }, + ]; expect(testResultsWithLowerCaseWindowsDriveLetters(testResults)).toEqual([ { name: 'c:\\drive\\is\\lowercase' }, { name: 'd:\\drive\\is\\uppercase' }, - ]) - }) - }) + ]); + }); + }); describe('coverageMapWithLowerCaseWindowsDriveLetters', () => { it('should return nothing when coverage was not collected', () => { - const data: any = {} - expect(coverageMapWithLowerCaseWindowsDriveLetters(data)).toBeUndefined() - }) + const data: any = {}; + expect(coverageMapWithLowerCaseWindowsDriveLetters(data)).toBeUndefined(); + }); it('should normalizes paths in the coverage map when collected', () => { const data: any = { @@ -117,7 +124,7 @@ describe('TestResult', () => { property: {}, }, }, - } + }; expect(coverageMapWithLowerCaseWindowsDriveLetters(data)).toEqual({ 'c:\\drive\\is\\lowercase': { path: 'c:\\drive\\is\\lowercase', @@ -127,19 +134,19 @@ describe('TestResult', () => { path: 'd:\\drive\\is\\uppercase', property: {}, }, - }) - }) - }) + }); + }); + }); describe('withLowerCaseDriveLetter', () => { it('should return a new file path when provided a path with an upper case drive letter', () => { - const filePath = 'C:\\path\\file.ext' - expect(withLowerCaseWindowsDriveLetter(filePath)).toBe('c:\\path\\file.ext') - }) + const filePath = 'C:\\path\\file.ext'; + expect(withLowerCaseWindowsDriveLetter(filePath)).toBe('c:\\path\\file.ext'); + }); it('should indicate no change is required otherwise', () => { - const filePath = 'c:\\path\\file.ext' - expect(withLowerCaseWindowsDriveLetter(filePath)).toBeUndefined() - }) - }) -}) + const filePath = 'c:\\path\\file.ext'; + expect(withLowerCaseWindowsDriveLetter(filePath)).toBeUndefined(); + }); + }); +}); diff --git a/tests/TestResults/TestResultProvider.test.ts b/tests/TestResults/TestResultProvider.test.ts index a310a1b34..82a1ea1b9 100644 --- a/tests/TestResults/TestResultProvider.test.ts +++ b/tests/TestResults/TestResultProvider.test.ts @@ -1,42 +1,42 @@ -jest.unmock('../../src/TestResults/TestResultProvider') +jest.unmock('../../src/TestResults/TestResultProvider'); -const updateFileWithJestStatus = jest.fn() -const assertionsForTestFile = jest.fn() +const updateFileWithJestStatus = jest.fn(); +const assertionsForTestFile = jest.fn(); jest.mock('jest-editor-support', () => { class TestReconciler { - assertionsForTestFile: jest.Mock - updateFileWithJestStatus: jest.Mock + assertionsForTestFile: jest.Mock; + updateFileWithJestStatus: jest.Mock; constructor() { - this.assertionsForTestFile = assertionsForTestFile - this.updateFileWithJestStatus = updateFileWithJestStatus + this.assertionsForTestFile = assertionsForTestFile; + this.updateFileWithJestStatus = updateFileWithJestStatus; } } - const parse = jest.fn() + const parse = jest.fn(); - return { TestReconciler, parse } -}) + return { TestReconciler, parse }; +}); const pathProperties = { sep: jest.fn(), -} +}; jest.mock('path', () => { - const path = {} + const path = {}; Object.defineProperty(path, 'sep', { get: () => pathProperties.sep(), - }) + }); - return path -}) + return path; +}); -import { TestResultProvider } from '../../src/TestResults/TestResultProvider' -import { TestReconciliationState } from '../../src/TestResults' -import { parseTest } from '../../src/TestParser' +import { TestResultProvider } from '../../src/TestResults/TestResultProvider'; +import { TestReconciliationState } from '../../src/TestResults'; +import { parseTest } from '../../src/TestParser'; describe('TestResultProvider', () => { describe('getResults()', () => { - const filePath = 'file.js' + const filePath = 'file.js'; const testBlock = { name: 'test name', start: { @@ -47,102 +47,102 @@ describe('TestResultProvider', () => { line: 4, column: 5, }, - } + }; const assertion = { title: testBlock.name, status: TestReconciliationState.KnownFail, terseMessage: 'terseMessage', shortMessage: 'shortMesage', line: 3, - } + }; beforeEach(() => { - jest.resetAllMocks() - }) + jest.resetAllMocks(); + }); it('should return the cached results if possible', () => { - const sut = new TestResultProvider() - ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + const sut = new TestResultProvider(); + ((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ itBlocks: [], - }) - assertionsForTestFile.mockReturnValueOnce([]) - const expected = sut.getResults(filePath) + }); + assertionsForTestFile.mockReturnValueOnce([]); + const expected = sut.getResults(filePath); - expect(sut.getResults(filePath)).toBe(expected) - }) + expect(sut.getResults(filePath)).toBe(expected); + }); it('should re-index the line and column number to zero-based', () => { - const sut = new TestResultProvider() - ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + const sut = new TestResultProvider(); + ((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ itBlocks: [testBlock], - }) - assertionsForTestFile.mockReturnValueOnce([assertion]) - const actual = sut.getResults(filePath) + }); + assertionsForTestFile.mockReturnValueOnce([assertion]); + const actual = sut.getResults(filePath); - expect(actual).toHaveLength(1) - expect(actual[0].lineNumberOfError).toBe(assertion.line - 1) + expect(actual).toHaveLength(1); + expect(actual[0].lineNumberOfError).toBe(assertion.line - 1); expect(actual[0].start).toEqual({ line: testBlock.start.line - 1, column: testBlock.start.column - 1, - }) + }); expect(actual[0].end).toEqual({ line: testBlock.end.line - 1, column: testBlock.end.column - 1, - }) - }) + }); + }); it('should look up the test result by line number only if the name matches', () => { - const sut = new TestResultProvider() - ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + const sut = new TestResultProvider(); + ((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ itBlocks: [testBlock], - }) - const assertionC = { ...assertion } - assertionC.title = 'xxx' - assertionsForTestFile.mockReturnValueOnce([assertionC]) - const actual = sut.getResults(filePath) - expect(actual).toHaveLength(1) - expect(actual[0].status).toBe(TestReconciliationState.Unknown) - }) + }); + const assertionC = { ...assertion }; + assertionC.title = 'xxx'; + assertionsForTestFile.mockReturnValueOnce([assertionC]); + const actual = sut.getResults(filePath); + expect(actual).toHaveLength(1); + expect(actual[0].status).toBe(TestReconciliationState.Unknown); + }); it('should look up the test result by test name', () => { - const sut = new TestResultProvider() - ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + const sut = new TestResultProvider(); + ((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ itBlocks: [testBlock], - }) - const assertionC = { ...assertion } - assertionC.line = undefined - assertionsForTestFile.mockReturnValueOnce([assertionC]) - const actual = sut.getResults(filePath) - - expect(actual).toHaveLength(1) - expect(actual[0].name).toBe(testBlock.name) - expect(actual[0].status).toBe(assertionC.status) - expect(actual[0].shortMessage).toBe(assertionC.shortMessage) - expect(actual[0].terseMessage).toBe(assertionC.terseMessage) - expect(actual[0].lineNumberOfError).toEqual(testBlock.end.line - 1) + }); + const assertionC = { ...assertion }; + assertionC.line = undefined; + assertionsForTestFile.mockReturnValueOnce([assertionC]); + const actual = sut.getResults(filePath); + + expect(actual).toHaveLength(1); + expect(actual[0].name).toBe(testBlock.name); + expect(actual[0].status).toBe(assertionC.status); + expect(actual[0].shortMessage).toBe(assertionC.shortMessage); + expect(actual[0].terseMessage).toBe(assertionC.terseMessage); + expect(actual[0].lineNumberOfError).toEqual(testBlock.end.line - 1); expect(actual[0].start).toEqual({ line: testBlock.start.line - 1, column: testBlock.start.column - 1, - }) + }); expect(actual[0].end).toEqual({ line: testBlock.end.line - 1, column: testBlock.end.column - 1, - }) - }) + }); + }); it('should use default values for unmatched assertions', () => { - const sut = new TestResultProvider() - ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + const sut = new TestResultProvider(); + ((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ itBlocks: [testBlock], - }) - assertionsForTestFile.mockReturnValueOnce([]) - const actual = sut.getResults(filePath) - - expect(actual).toHaveLength(1) - expect(actual[0].status).toBe(TestReconciliationState.Unknown) - expect(actual[0].shortMessage).toBeUndefined() - expect(actual[0].terseMessage).toBeUndefined() - }) + }); + assertionsForTestFile.mockReturnValueOnce([]); + const actual = sut.getResults(filePath); + + expect(actual).toHaveLength(1); + expect(actual[0].status).toBe(TestReconciliationState.Unknown); + expect(actual[0].shortMessage).toBeUndefined(); + expect(actual[0].terseMessage).toBeUndefined(); + }); describe('duplicate test names', () => { const testBlock2 = { ...testBlock, @@ -154,7 +154,7 @@ describe('TestResultProvider', () => { line: 7, column: 5, }, - } + }; const testBlock3 = { ...testBlock, start: { @@ -165,33 +165,33 @@ describe('TestResultProvider', () => { line: 25, column: 5, }, - } - beforeEach(() => {}) + }; + beforeEach(() => {}); it('can resolve by matching error line', () => { - ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + ((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ itBlocks: [testBlock, testBlock2], - }) + }); - const sut = new TestResultProvider() + const sut = new TestResultProvider(); assertionsForTestFile.mockReturnValueOnce([ assertion, { title: testBlock.name, status: TestReconciliationState.KnownSuccess, }, - ]) - const actual = sut.getResults(filePath) + ]); + const actual = sut.getResults(filePath); - expect(actual).toHaveLength(2) - expect(actual[0].status).toBe(TestReconciliationState.KnownFail) - expect(actual[1].status).toBe(TestReconciliationState.KnownSuccess) - }) + expect(actual).toHaveLength(2); + expect(actual[0].status).toBe(TestReconciliationState.KnownFail); + expect(actual[1].status).toBe(TestReconciliationState.KnownSuccess); + }); it('can resolve even if these tests pass, i.e. no line number', () => { - ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + ((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ itBlocks: [testBlock2, testBlock3], - }) + }); - const sut = new TestResultProvider() + const sut = new TestResultProvider(); assertionsForTestFile.mockReturnValueOnce([ { title: testBlock.name, @@ -202,18 +202,20 @@ describe('TestResultProvider', () => { status: TestReconciliationState.KnownSuccess, location: { colum: 3, line: 22 }, }, - ]) - const actual = sut.getResults(filePath) + ]); + const actual = sut.getResults(filePath); - expect(actual).toHaveLength(2) - expect(actual.every((r) => r.status === TestReconciliationState.KnownSuccess)).toEqual(true) - }) + expect(actual).toHaveLength(2); + expect(actual.every((r) => r.status === TestReconciliationState.KnownSuccess)).toEqual( + true + ); + }); it('default to unknown if failed to match by line or location', () => { - ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + ((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ itBlocks: [testBlock2, testBlock3], - }) + }); - const sut = new TestResultProvider() + const sut = new TestResultProvider(); assertionsForTestFile.mockReturnValueOnce([ { title: testBlock.name, @@ -223,16 +225,18 @@ describe('TestResultProvider', () => { title: testBlock.name, status: TestReconciliationState.KnownSuccess, }, - ]) - const actual = sut.getResults(filePath) - - expect(actual).toHaveLength(2) - expect(actual.every((r) => r.status === TestReconciliationState.Unknown)).toEqual(true) - expect(actual.every((r) => r.shortMessage && r.shortMessage.includes('duplicate test names'))).toEqual(true) - }) - }) + ]); + const actual = sut.getResults(filePath); + + expect(actual).toHaveLength(2); + expect(actual.every((r) => r.status === TestReconciliationState.Unknown)).toEqual(true); + expect( + actual.every((r) => r.shortMessage && r.shortMessage.includes('duplicate test names')) + ).toEqual(true); + }); + }); it('should only mark error line number if it is within the right itBlock', () => { - const sut = new TestResultProvider() + const sut = new TestResultProvider(); const testBlock2 = { name: 'test2', start: { @@ -243,23 +247,25 @@ describe('TestResultProvider', () => { line: 7, column: 5, }, - } - ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + }; + ((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ itBlocks: [testBlock, testBlock2], - }) + }); assertionsForTestFile.mockReturnValueOnce([ { title: testBlock2.name, status: TestReconciliationState.KnownSuccess, line: 3, }, - ]) - const actual = sut.getResults(filePath) + ]); + const actual = sut.getResults(filePath); - expect(actual).toHaveLength(2) - expect(actual.some((a) => a.name === testBlock.name && a.status === TestReconciliationState.Unknown)).toEqual( - true - ) + expect(actual).toHaveLength(2); + expect( + actual.some( + (a) => a.name === testBlock.name && a.status === TestReconciliationState.Unknown + ) + ).toEqual(true); expect( actual.some( (a) => @@ -267,8 +273,8 @@ describe('TestResultProvider', () => { a.status === TestReconciliationState.KnownSuccess && a.lineNumberOfError === testBlock2.end.line - 1 ) - ).toEqual(true) - }) + ).toEqual(true); + }); describe('template literal handling', () => { const testBlock2 = { @@ -283,60 +289,68 @@ describe('TestResultProvider', () => { line: 7, column: 5, }, - } + }; const useTests = (itBlocks = [testBlock, testBlock2]) => { - ;((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ + ((parseTest as unknown) as jest.Mock<{}>).mockReturnValueOnce({ itBlocks, - }) - } + }); + }; beforeEach(() => { - jest.resetAllMocks() - }) + jest.resetAllMocks(); + }); it(`find test by assertion error line`, () => { - const sut = new TestResultProvider() + const sut = new TestResultProvider(); - useTests() + useTests(); assertionsForTestFile.mockReturnValueOnce([ { title: 'template literal 2', status: TestReconciliationState.KnownFail, line: 6, }, - ]) - const actual = sut.getResults(filePath) + ]); + const actual = sut.getResults(filePath); - expect(actual).toHaveLength(2) - expect(actual.some((a) => a.status === TestReconciliationState.Unknown && a.name === testBlock.name)).toEqual( - true - ) + expect(actual).toHaveLength(2); + expect( + actual.some( + (a) => a.status === TestReconciliationState.Unknown && a.name === testBlock.name + ) + ).toEqual(true); expect( - actual.some((a) => a.status === TestReconciliationState.KnownFail && a.name === testBlock2.name) - ).toEqual(true) - }) + actual.some( + (a) => a.status === TestReconciliationState.KnownFail && a.name === testBlock2.name + ) + ).toEqual(true); + }); it(`find test by assertion location`, () => { - const sut = new TestResultProvider() + const sut = new TestResultProvider(); - useTests() + useTests(); assertionsForTestFile.mockReturnValueOnce([ { title: 'template literal 2', status: TestReconciliationState.KnownSuccess, location: { colum: 3, line: 6 }, }, - ]) - const actual = sut.getResults(filePath) + ]); + const actual = sut.getResults(filePath); - expect(actual).toHaveLength(2) - expect(actual.some((a) => a.status === TestReconciliationState.Unknown && a.name === testBlock.name)).toEqual( - true - ) + expect(actual).toHaveLength(2); expect( - actual.some((a) => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock2.name) - ).toEqual(true) - }) + actual.some( + (a) => a.status === TestReconciliationState.Unknown && a.name === testBlock.name + ) + ).toEqual(true); + expect( + actual.some( + (a) => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock2.name + ) + ).toEqual(true); + }); it(`find test by partial name match`, () => { - const sut = new TestResultProvider() - useTests() + const sut = new TestResultProvider(); + useTests(); assertionsForTestFile.mockReturnValueOnce([ { @@ -347,21 +361,25 @@ describe('TestResultProvider', () => { title: 'template literal 2', status: TestReconciliationState.KnownSuccess, }, - ]) - const actual = sut.getResults(filePath) - expect(actual).toHaveLength(2) - expect(actual.some((a) => a.status === TestReconciliationState.Unknown && a.name === testBlock.name)).toEqual( - true - ) + ]); + const actual = sut.getResults(filePath); + expect(actual).toHaveLength(2); + expect( + actual.some( + (a) => a.status === TestReconciliationState.Unknown && a.name === testBlock.name + ) + ).toEqual(true); expect( - actual.some((a) => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock2.name) - ).toEqual(true) - }) + actual.some( + (a) => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock2.name + ) + ).toEqual(true); + }); it(`multiple template literals`, () => { - const sut = new TestResultProvider() + const sut = new TestResultProvider(); // tslint:disable-next-line: no-invalid-template-strings - const testBlock3 = { ...testBlock2, name: 'template literal ${i}, ${k}: {something}' } - useTests([testBlock3, testBlock2]) + const testBlock3 = { ...testBlock2, name: 'template literal ${i}, ${k}: {something}' }; + useTests([testBlock3, testBlock2]); assertionsForTestFile.mockReturnValueOnce([ { title: 'template literal I got something like this', @@ -371,65 +389,69 @@ describe('TestResultProvider', () => { title: 'template literal 1, 2: {something}', status: TestReconciliationState.KnownSuccess, }, - ]) - const actual = sut.getResults(filePath) - expect(actual).toHaveLength(2) + ]); + const actual = sut.getResults(filePath); + expect(actual).toHaveLength(2); expect( - actual.some((a) => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock3.name) - ).toEqual(true) + actual.some( + (a) => a.status === TestReconciliationState.KnownSuccess && a.name === testBlock3.name + ) + ).toEqual(true); expect( - actual.some((a) => a.status === TestReconciliationState.KnownFail && a.name === testBlock2.name) - ).toEqual(true) - }) + actual.some( + (a) => a.status === TestReconciliationState.KnownFail && a.name === testBlock2.name + ) + ).toEqual(true); + }); describe('when match failed', () => { - const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}) + const consoleSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); beforeEach(() => { - jest.resetAllMocks() - useTests([testBlock2]) + jest.resetAllMocks(); + useTests([testBlock2]); assertionsForTestFile.mockReturnValueOnce([ { title: 'template literals 2', status: TestReconciliationState.KnownSuccess, }, - ]) - }) + ]); + }); it(`will report error`, () => { - const sut = new TestResultProvider() - const actual = sut.getResults(filePath) - - expect(actual).toHaveLength(1) - expect(actual[0].status).toBe(TestReconciliationState.Unknown) - expect(actual[0].shortMessage).not.toBeUndefined() - expect(consoleSpy).not.toHaveBeenCalled() - }) + const sut = new TestResultProvider(); + const actual = sut.getResults(filePath); + + expect(actual).toHaveLength(1); + expect(actual[0].status).toBe(TestReconciliationState.Unknown); + expect(actual[0].shortMessage).not.toBeUndefined(); + expect(consoleSpy).not.toHaveBeenCalled(); + }); it('will also output to console in verbose mode', () => { - const sut = new TestResultProvider(true) - const actual = sut.getResults(filePath) - - expect(actual).toHaveLength(1) - expect(actual[0].status).toBe(TestReconciliationState.Unknown) - expect(actual[0].shortMessage).not.toBeUndefined() - expect(consoleSpy).toHaveBeenCalled() - }) - }) - }) - }) + const sut = new TestResultProvider(true); + const actual = sut.getResults(filePath); + + expect(actual).toHaveLength(1); + expect(actual[0].status).toBe(TestReconciliationState.Unknown); + expect(actual[0].shortMessage).not.toBeUndefined(); + expect(consoleSpy).toHaveBeenCalled(); + }); + }); + }); + }); describe('getSortedResults()', () => { - const filePath = 'file.js' + const filePath = 'file.js'; it('should return cached results if possible', () => { - const sut = new TestResultProvider() - sut.getResults = jest.fn().mockReturnValueOnce([]) - const expected = sut.getSortedResults(filePath) + const sut = new TestResultProvider(); + sut.getResults = jest.fn().mockReturnValueOnce([]); + const expected = sut.getSortedResults(filePath); - expect(sut.getSortedResults(filePath)).toBe(expected) - }) + expect(sut.getSortedResults(filePath)).toBe(expected); + }); it('should return the sorted test results', () => { - const sut = new TestResultProvider() + const sut = new TestResultProvider(); sut.getResults = jest .fn() .mockReturnValueOnce([ @@ -437,35 +459,35 @@ describe('TestResultProvider', () => { { status: TestReconciliationState.KnownSkip }, { status: TestReconciliationState.KnownSuccess }, { status: TestReconciliationState.Unknown }, - ]) + ]); expect(sut.getSortedResults(filePath)).toEqual({ fail: [{ status: TestReconciliationState.KnownFail }], skip: [{ status: TestReconciliationState.KnownSkip }], success: [{ status: TestReconciliationState.KnownSuccess }], unknown: [{ status: TestReconciliationState.Unknown }], - }) - }) - }) + }); + }); + }); describe('updateTestResults()', () => { it('should reset the cache', () => { - const sut = new TestResultProvider() - const results: any = {} - sut.resetCache = jest.fn() - sut.updateTestResults(results) + const sut = new TestResultProvider(); + const results: any = {}; + sut.resetCache = jest.fn(); + sut.updateTestResults(results); - expect(sut.resetCache).toBeCalled() - }) + expect(sut.resetCache).toBeCalled(); + }); it('should update the cached file status', () => { - const expected: any = {} - updateFileWithJestStatus.mockReturnValueOnce(expected) + const expected: any = {}; + updateFileWithJestStatus.mockReturnValueOnce(expected); - const sut = new TestResultProvider() - const results: any = {} + const sut = new TestResultProvider(); + const results: any = {}; - expect(sut.updateTestResults(results)).toBe(expected) - expect(updateFileWithJestStatus).toBeCalledWith(results) - }) - }) -}) + expect(sut.updateTestResults(results)).toBe(expected); + expect(updateFileWithJestStatus).toBeCalledWith(results); + }); + }); +}); diff --git a/tests/decorations.test.ts b/tests/decorations.test.ts index 978452060..3194b909d 100644 --- a/tests/decorations.test.ts +++ b/tests/decorations.test.ts @@ -1,4 +1,4 @@ -jest.unmock('../src/decorations') +jest.unmock('../src/decorations'); jest.mock('vscode', () => { return { DecorationRangeBehavior: { @@ -8,37 +8,37 @@ jest.mock('vscode', () => { window: { createTextEditorDecorationType: jest.fn(), }, - } -}) + }; +}); -import { passingItName, failingItName, skipItName, notRanItName } from '../src/decorations' -import * as vscode from 'vscode' +import { passingItName, failingItName, skipItName, notRanItName } from '../src/decorations'; +import * as vscode from 'vscode'; function testRangeBehavior(factoryMethod: () => void) { it('should set the range behavior', () => { - const mock = (vscode.window.createTextEditorDecorationType as unknown) as jest.Mock<{}> - mock.mockReset() - factoryMethod() + const mock = (vscode.window.createTextEditorDecorationType as unknown) as jest.Mock<{}>; + mock.mockReset(); + factoryMethod(); - expect(mock.mock.calls).toHaveLength(1) - expect(mock.mock.calls[0][0].rangeBehavior).toBe(vscode.DecorationRangeBehavior.ClosedClosed) - }) + expect(mock.mock.calls).toHaveLength(1); + expect(mock.mock.calls[0][0].rangeBehavior).toBe(vscode.DecorationRangeBehavior.ClosedClosed); + }); } describe('Test Result Annotations', () => { describe('Pass', () => { - testRangeBehavior(passingItName) - }) + testRangeBehavior(passingItName); + }); describe('Fail', () => { - testRangeBehavior(failingItName) - }) + testRangeBehavior(failingItName); + }); describe('Skip', () => { - testRangeBehavior(skipItName) - }) + testRangeBehavior(skipItName); + }); describe('Unknown', () => { - testRangeBehavior(notRanItName) - }) -}) + testRangeBehavior(notRanItName); + }); +}); diff --git a/tests/diagnostics.test.ts b/tests/diagnostics.test.ts index 5d161e9e4..64375008b 100644 --- a/tests/diagnostics.test.ts +++ b/tests/diagnostics.test.ts @@ -1,41 +1,50 @@ -jest.unmock('../src/diagnostics') -import { updateDiagnostics, updateCurrentDiagnostics, resetDiagnostics, failedSuiteCount } from '../src/diagnostics' -import * as vscode from 'vscode' -import { TestFileAssertionStatus, TestReconcilationState, TestAssertionStatus } from 'jest-editor-support' -import { TestResult, TestReconciliationState } from '../src/TestResults' +jest.unmock('../src/diagnostics'); +import { + updateDiagnostics, + updateCurrentDiagnostics, + resetDiagnostics, + failedSuiteCount, +} from '../src/diagnostics'; +import * as vscode from 'vscode'; +import { + TestFileAssertionStatus, + TestReconcilationState, + TestAssertionStatus, +} from 'jest-editor-support'; +import { TestResult, TestReconciliationState } from '../src/TestResults'; class MockDiagnosticCollection implements vscode.DiagnosticCollection { - name = 'test' - set = jest.fn() - delete = jest.fn() - clear = jest.fn() - forEach = jest.fn() - get = jest.fn() - has = jest.fn() - dispose = jest.fn() + name = 'test'; + set = jest.fn(); + delete = jest.fn(); + clear = jest.fn(); + forEach = jest.fn(); + get = jest.fn(); + has = jest.fn(); + dispose = jest.fn(); } -vscode.window.visibleTextEditors = [] +vscode.window.visibleTextEditors = []; // tslint:disable no-console describe('test diagnostics', () => { describe('resetDiagnostics', () => { it('will clear given diagnostics', () => { - const mockDiagnostics = new MockDiagnosticCollection() - resetDiagnostics(mockDiagnostics) - expect(mockDiagnostics.clear).toBeCalled() - }) - }) + const mockDiagnostics = new MockDiagnosticCollection(); + resetDiagnostics(mockDiagnostics); + expect(mockDiagnostics.clear).toBeCalled(); + }); + }); // vscode component validation helper function validateRange(args: any[], startLine: number, startCharacter: number) { - expect(args[0]).toEqual(startLine) - expect(args[1]).toEqual(startCharacter) + expect(args[0]).toEqual(startLine); + expect(args[1]).toEqual(startCharacter); } describe('updateDiagnostics', () => { - const consoleWarn = console.warn - let lineNumber = 17 + const consoleWarn = console.warn; + let lineNumber = 17; function createAssertion(title: string, status: TestReconcilationState): TestAssertionStatus { return { @@ -43,37 +52,37 @@ describe('test diagnostics', () => { status, message: `${title} ${status}`, line: lineNumber++, - } + }; } function createTestResult( file: string, assertions: TestAssertionStatus[], status: TestReconcilationState = 'KnownFail' ): TestFileAssertionStatus { - return { file, message: `${file}:${status}`, status, assertions } + return { file, message: `${file}:${status}`, status, assertions }; } function validateDiagnostic(args: any[], message: string, severity: vscode.DiagnosticSeverity) { - expect(args[1]).toEqual(message) - expect(args[2]).toEqual(severity) + expect(args[1]).toEqual(message); + expect(args[2]).toEqual(severity); } beforeEach(() => { - jest.resetAllMocks() - console.warn = consoleWarn - }) + jest.resetAllMocks(); + console.warn = consoleWarn; + }); it('can handle when all tests passed', () => { - const mockDiagnostics = new MockDiagnosticCollection() + const mockDiagnostics = new MockDiagnosticCollection(); - updateDiagnostics([], mockDiagnostics) - expect(mockDiagnostics.clear).not.toBeCalled() - expect(mockDiagnostics.set).not.toBeCalled() - }) + updateDiagnostics([], mockDiagnostics); + expect(mockDiagnostics.clear).not.toBeCalled(); + expect(mockDiagnostics.set).not.toBeCalled(); + }); it('ensures non-negative line number in diagnostic message', () => { - const mockDiagnostics = new MockDiagnosticCollection() + const mockDiagnostics = new MockDiagnosticCollection(); - console.warn = jest.fn() + console.warn = jest.fn(); const testResult = createTestResult('mocked-test-file.js', [ { title: 'should be valid', @@ -81,13 +90,13 @@ describe('test diagnostics', () => { message: 'failing reason', line: -100, }, - ]) - updateDiagnostics([testResult], mockDiagnostics) - expect(vscode.Range).toHaveBeenCalledWith(0, 0, 0, Number.MAX_SAFE_INTEGER) - }) + ]); + updateDiagnostics([testResult], mockDiagnostics); + expect(vscode.Range).toHaveBeenCalledWith(0, 0, 0, Number.MAX_SAFE_INTEGER); + }); it('uses shortMessage format to display error details', () => { - const mockDiagnostics = new MockDiagnosticCollection() + const mockDiagnostics = new MockDiagnosticCollection(); const testResult = createTestResult('mocked-test-file.js', [ { @@ -106,9 +115,9 @@ describe('test diagnostics', () => { terseMessage: `Expected: 2, Received: 1`, line: 123, }, - ]) - updateDiagnostics([testResult], mockDiagnostics) - expect(vscode.Diagnostic).toHaveBeenCalledTimes(1) + ]); + updateDiagnostics([testResult], mockDiagnostics); + expect(vscode.Diagnostic).toHaveBeenCalledTimes(1); expect(vscode.Diagnostic).toHaveBeenCalledWith( expect.anything(), `expect(received).toBe(expected) // Object.is equality @@ -116,12 +125,15 @@ describe('test diagnostics', () => { Expected: 2 Received: 1`, expect.anything() - ) - }) + ); + }); it('can update diagnostics from mixed test results', () => { const allTests = [ - createTestResult('f1', [createAssertion('a1', 'KnownFail'), createAssertion('a2', 'KnownFail')]), + createTestResult('f1', [ + createAssertion('a1', 'KnownFail'), + createAssertion('a2', 'KnownFail'), + ]), createTestResult('f2', [ createAssertion('a3', 'KnownFail'), createAssertion('a4', 'KnownSuccess'), @@ -130,122 +142,132 @@ describe('test diagnostics', () => { createTestResult('f3', []), createTestResult('s4', [createAssertion('a6', 'KnownSuccess')], 'KnownSuccess'), createTestResult('s5', [], 'Unknown'), - ] - const failedTestSuiteCount = allTests.reduce((sum, t) => sum + (t.status === 'KnownFail' ? 1 : 0), 0) - const notFailedTestSuiteCount = allTests.reduce((sum, t) => sum + (t.status !== 'KnownFail' ? 1 : 0), 0) + ]; + const failedTestSuiteCount = allTests.reduce( + (sum, t) => sum + (t.status === 'KnownFail' ? 1 : 0), + 0 + ); + const notFailedTestSuiteCount = allTests.reduce( + (sum, t) => sum + (t.status !== 'KnownFail' ? 1 : 0), + 0 + ); const failedAssertionCount = allTests .filter((t) => t.status === 'KnownFail') .map((f) => f.assertions.filter((a) => (a.status = 'KnownFail'))) - .reduce((sum, assertions) => sum + assertions.length, 0) + .reduce((sum, assertions) => sum + assertions.length, 0); const failedTestWithoutAssertionCount = allTests.reduce( (sum, t) => sum + (t.status === 'KnownFail' && t.assertions.length === 0 ? 1 : 0), 0 - ) - const mockDiagnostics = new MockDiagnosticCollection() - updateDiagnostics(allTests, mockDiagnostics) + ); + const mockDiagnostics = new MockDiagnosticCollection(); + updateDiagnostics(allTests, mockDiagnostics); // verified diagnostics are added for all failed tests including files failed to run - expect(mockDiagnostics.set).toHaveBeenCalledTimes(failedTestSuiteCount) - expect(vscode.Range).toHaveBeenCalledTimes(failedAssertionCount + failedTestWithoutAssertionCount) - expect(vscode.Diagnostic).toHaveBeenCalledTimes(failedAssertionCount + failedTestWithoutAssertionCount) + expect(mockDiagnostics.set).toHaveBeenCalledTimes(failedTestSuiteCount); + expect(vscode.Range).toHaveBeenCalledTimes( + failedAssertionCount + failedTestWithoutAssertionCount + ); + expect(vscode.Diagnostic).toHaveBeenCalledTimes( + failedAssertionCount + failedTestWithoutAssertionCount + ); // verify correctly reported error content - const setCalls = mockDiagnostics.set.mock.calls - const rangeCalls = (vscode.Range as jest.Mock<any>).mock.calls - const diagCalls = (vscode.Diagnostic as jest.Mock<any>).mock.calls + const setCalls = mockDiagnostics.set.mock.calls; + const rangeCalls = (vscode.Range as jest.Mock<any>).mock.calls; + const diagCalls = (vscode.Diagnostic as jest.Mock<any>).mock.calls; // validate the diagnosis produced - let assertion = 0 + let assertion = 0; for (let i = 0; i < allTests.length; i++) { - const f = allTests[i] + const f = allTests[i]; if (f.status !== 'KnownFail') { - continue + continue; } - expect(setCalls[i][0].indexOf(f.file)).toBeGreaterThanOrEqual(0) + expect(setCalls[i][0].indexOf(f.file)).toBeGreaterThanOrEqual(0); if (f.assertions.length <= 0) { - const rCall = rangeCalls[assertion] - const dCall = diagCalls[assertion] - validateDiagnostic(dCall, f.message, vscode.DiagnosticSeverity.Error) - validateRange(rCall, 0, 0) - assertion++ + const rCall = rangeCalls[assertion]; + const dCall = diagCalls[assertion]; + validateDiagnostic(dCall, f.message, vscode.DiagnosticSeverity.Error); + validateRange(rCall, 0, 0); + assertion++; } else { f.assertions.forEach((a) => { - const rCall = rangeCalls[assertion] - const dCall = diagCalls[assertion] + const rCall = rangeCalls[assertion]; + const dCall = diagCalls[assertion]; - validateDiagnostic(dCall, a.message, vscode.DiagnosticSeverity.Error) - validateRange(rCall, a.line - 1, 0) - assertion++ - }) + validateDiagnostic(dCall, a.message, vscode.DiagnosticSeverity.Error); + validateRange(rCall, a.line - 1, 0); + assertion++; + }); } } // verify: removed passed tests - expect(mockDiagnostics.delete).toHaveBeenCalledTimes(notFailedTestSuiteCount) - }) + expect(mockDiagnostics.delete).toHaveBeenCalledTimes(notFailedTestSuiteCount); + }); it('knows how many failed suite from diagnostics', () => { - const mockDiagnostics = new MockDiagnosticCollection() - const invokeCount = 7 + const mockDiagnostics = new MockDiagnosticCollection(); + const invokeCount = 7; mockDiagnostics.forEach.mockImplementation((f) => { for (let i = 0; i < invokeCount; i++) { - f({}) + f({}); } - }) + }); - expect(failedSuiteCount(mockDiagnostics)).toEqual(invokeCount) - }) + expect(failedSuiteCount(mockDiagnostics)).toEqual(invokeCount); + }); it('should not produce negative diagnostic range', () => { - const mockDiagnostics = new MockDiagnosticCollection() - const assertion = createAssertion('a', 'KnownFail') - const invalidLine = [0, -1, undefined, null, NaN] - console.warn = jest.fn() + const mockDiagnostics = new MockDiagnosticCollection(); + const assertion = createAssertion('a', 'KnownFail'); + const invalidLine = [0, -1, undefined, null, NaN]; + console.warn = jest.fn(); invalidLine.forEach((line) => { - jest.clearAllMocks() + jest.clearAllMocks(); - assertion.line = line - const tests = [createTestResult('f', [assertion])] - updateDiagnostics(tests, mockDiagnostics) + assertion.line = line; + const tests = [createTestResult('f', [assertion])]; + updateDiagnostics(tests, mockDiagnostics); - const rangeCalls = (vscode.Range as jest.Mock<any>).mock.calls - expect(rangeCalls.length).toEqual(1) - validateRange(rangeCalls[0], 0, 0) - }) - }) - }) + const rangeCalls = (vscode.Range as jest.Mock<any>).mock.calls; + expect(rangeCalls.length).toEqual(1); + validateRange(rangeCalls[0], 0, 0); + }); + }); + }); describe('updateCurrentDiagnostics', () => { - const mockLineAt = jest.fn() - const range = new vscode.Range(3, 0, 3, 15) - let mockEditor + const mockLineAt = jest.fn(); + const range = new vscode.Range(3, 0, 3, 15); + let mockEditor; beforeEach(() => { - jest.resetAllMocks() + jest.resetAllMocks(); - mockLineAt.mockReturnValueOnce({ range }) + mockLineAt.mockReturnValueOnce({ range }); mockEditor = { document: { uri: { fsPath: `file://a/b/c.ts` }, lineAt: mockLineAt, }, - } - console.warn = jest.fn() - }) + }; + console.warn = jest.fn(); + }); it('will remove diagnosis if no failed test', () => { - const mockDiagnostics = new MockDiagnosticCollection() - updateCurrentDiagnostics([], mockDiagnostics, mockEditor as any) - expect(mockDiagnostics.set).not.toHaveBeenCalled() - expect(mockDiagnostics.delete).toHaveBeenCalled() - }) + const mockDiagnostics = new MockDiagnosticCollection(); + updateCurrentDiagnostics([], mockDiagnostics, mockEditor as any); + expect(mockDiagnostics.set).not.toHaveBeenCalled(); + expect(mockDiagnostics.delete).toHaveBeenCalled(); + }); it('can display diagnosis based on the current editor itBlock info', () => { - const mockDiagnostics = new MockDiagnosticCollection() - const msg = 'a short error message' + const mockDiagnostics = new MockDiagnosticCollection(); + const msg = 'a short error message'; const testBlock: TestResult = { name: 'a', start: { line: 2, column: 3 }, @@ -253,13 +275,13 @@ describe('test diagnostics', () => { lineNumberOfError: 3, shortMessage: msg, status: TestReconciliationState.KnownFail, - } + }; - updateCurrentDiagnostics([testBlock], mockDiagnostics, mockEditor as any) + updateCurrentDiagnostics([testBlock], mockDiagnostics, mockEditor as any); - expect(mockDiagnostics.set).toHaveBeenCalledTimes(1) - expect(vscode.Diagnostic).toHaveBeenCalledTimes(1) - expect(vscode.Diagnostic).toHaveBeenCalledWith(range, msg, vscode.DiagnosticSeverity.Error) - }) - }) -}) + expect(mockDiagnostics.set).toHaveBeenCalledTimes(1); + expect(vscode.Diagnostic).toHaveBeenCalledTimes(1); + expect(vscode.Diagnostic).toHaveBeenCalledWith(range, msg, vscode.DiagnosticSeverity.Error); + }); + }); +}); diff --git a/tests/editor.test.ts b/tests/editor.test.ts index 813619073..8d590ad9c 100644 --- a/tests/editor.test.ts +++ b/tests/editor.test.ts @@ -1,61 +1,61 @@ -jest.unmock('../src/editor') +jest.unmock('../src/editor'); const vscodeProperties = { window: { activeTextEditor: jest.fn(), visibleTextEditors: jest.fn(), }, -} +}; jest.mock('vscode', () => { const vscode = { window: {}, - } + }; Object.defineProperty(vscode.window, 'visibleTextEditors', { get: () => vscodeProperties.window.visibleTextEditors(), - }) - return vscode -}) + }); + return vscode; +}); -import { hasDocument, isOpenInMultipleEditors } from '../src/editor' +import { hasDocument, isOpenInMultipleEditors } from '../src/editor'; describe('editor', () => { describe('hasDocument()', () => { it('should return false when the editor is falsy', () => { - const editor: any = undefined - expect(hasDocument(editor)).toBe(false) - }) + const editor: any = undefined; + expect(hasDocument(editor)).toBe(false); + }); it('should return false when the document is falsy', () => { - const editor: any = {} - expect(hasDocument(editor)).toBe(false) - }) + const editor: any = {}; + expect(hasDocument(editor)).toBe(false); + }); it('should return true when the document is defined', () => { - const editor: any = { document: {} } - expect(hasDocument(editor)).toBe(true) - }) - }) + const editor: any = { document: {} }; + expect(hasDocument(editor)).toBe(true); + }); + }); describe('isOpenInMultipleEditors()', () => { - const document: any = { fileName: 'fileName' } + const document: any = { fileName: 'fileName' }; it('should return false when the document is falsy', () => { - expect(isOpenInMultipleEditors(undefined)).toBe(false) - }) + expect(isOpenInMultipleEditors(undefined)).toBe(false); + }); it('should return false when the document is not matched', () => { - vscodeProperties.window.visibleTextEditors.mockReturnValueOnce([]) - expect(isOpenInMultipleEditors(document)).toBe(false) - }) + vscodeProperties.window.visibleTextEditors.mockReturnValueOnce([]); + expect(isOpenInMultipleEditors(document)).toBe(false); + }); it('should return false when the document is open once', () => { - vscodeProperties.window.visibleTextEditors.mockReturnValueOnce([{ document }]) - expect(isOpenInMultipleEditors(document)).toBe(false) - }) + vscodeProperties.window.visibleTextEditors.mockReturnValueOnce([{ document }]); + expect(isOpenInMultipleEditors(document)).toBe(false); + }); it('should return true when the document is open more than once', () => { - vscodeProperties.window.visibleTextEditors.mockReturnValueOnce([{ document }, { document }]) - expect(isOpenInMultipleEditors(document)).toBe(true) - }) - }) -}) + vscodeProperties.window.visibleTextEditors.mockReturnValueOnce([{ document }, { document }]); + expect(isOpenInMultipleEditors(document)).toBe(true); + }); + }); +}); diff --git a/tests/extension.test.ts b/tests/extension.test.ts index 4eab0e502..be014468a 100644 --- a/tests/extension.test.ts +++ b/tests/extension.test.ts @@ -1,24 +1,24 @@ -jest.unmock('../src/extension') +jest.unmock('../src/extension'); -const extensionName = 'jest' +const extensionName = 'jest'; jest.mock('../src/appGlobals', () => ({ extensionName, -})) +})); const statusBar = { register: jest.fn(() => []), -} -jest.mock('../src/StatusBar', () => ({ statusBar })) +}; +jest.mock('../src/StatusBar', () => ({ statusBar })); jest.mock('../src/Coverage', () => ({ registerCoverageCodeLens: jest.fn().mockReturnValue([]), CoverageCodeLensProvider: jest.fn().mockReturnValue({}), -})) +})); jest.mock('../src/SnapshotCodeLens', () => ({ registerSnapshotCodeLens: jest.fn(() => []), registerSnapshotPreview: jest.fn(() => []), -})) +})); const jestInstance = { toggleCoverageOverlay: jest.fn(), @@ -26,7 +26,7 @@ const jestInstance = { startProcess: jest.fn(), stopProcess: jest.fn(), restartProcess: jest.fn(), -} +}; const extensionManager = { register: jest.fn(), @@ -34,25 +34,35 @@ const extensionManager = { get: jest.fn().mockReturnValue(jestInstance), unregisterAll: jest.fn(), registerCommand: jest.fn().mockImplementation((...args) => args), -} +}; // tslint:disable-next-line: variable-name -const ExtensionManager = jest.fn().mockImplementation(() => extensionManager) +const ExtensionManager = jest.fn().mockImplementation(() => extensionManager); jest.mock('../src/extensionManager', () => ({ ExtensionManager, getExtensionWindowSettings: jest.fn(() => ({})), -})) - -import * as vscode from 'vscode' -import { activate, deactivate } from '../src/extension' -;(vscode.commands as any).registerCommand = jest.fn().mockImplementation((...args) => args) -;(vscode.window as any).onDidChangeActiveTextEditor = jest.fn().mockReturnValue('onDidChangeActiveTextEditor') -vscode.workspace.getWorkspaceFolder = jest.fn().mockReturnValue({ name: 'workspaceFolder1' }) -;(vscode.workspace as any).onDidChangeConfiguration = jest.fn().mockReturnValue('onDidChangeConfiguration') -;(vscode.workspace as any).onDidChangeTextDocument = jest.fn().mockReturnValue('onDidChangeTextDocument') -;(vscode.workspace as any).onDidChangeWorkspaceFolders = jest.fn().mockReturnValue('onDidChangeWorkspaceFolders') -;(vscode.workspace as any).onDidCloseTextDocument = jest.fn().mockReturnValue('onDidCloseTextDocument') +})); + +import * as vscode from 'vscode'; +import { activate, deactivate } from '../src/extension'; +(vscode.commands as any).registerCommand = jest.fn().mockImplementation((...args) => args); +(vscode.window as any).onDidChangeActiveTextEditor = jest + .fn() + .mockReturnValue('onDidChangeActiveTextEditor'); +vscode.workspace.getWorkspaceFolder = jest.fn().mockReturnValue({ name: 'workspaceFolder1' }); +(vscode.workspace as any).onDidChangeConfiguration = jest + .fn() + .mockReturnValue('onDidChangeConfiguration'); +(vscode.workspace as any).onDidChangeTextDocument = jest + .fn() + .mockReturnValue('onDidChangeTextDocument'); +(vscode.workspace as any).onDidChangeWorkspaceFolders = jest + .fn() + .mockReturnValue('onDidChangeWorkspaceFolders'); +(vscode.workspace as any).onDidCloseTextDocument = jest + .fn() + .mockReturnValue('onDidCloseTextDocument'); describe('Extension', () => { describe('activate()', () => { @@ -60,133 +70,135 @@ describe('Extension', () => { subscriptions: { push: jest.fn(), }, - } + }; beforeEach(() => { - context.subscriptions.push.mockReset() - }) + context.subscriptions.push.mockReset(); + }); it('should instantiate ExtensionManager', () => { - activate(context) - expect(ExtensionManager).toHaveBeenCalledTimes(1) - }) + activate(context); + expect(ExtensionManager).toHaveBeenCalledTimes(1); + }); it('should register statusBar', () => { - statusBar.register.mockClear() - activate(context) - expect(statusBar.register).toHaveBeenCalled() - }) + statusBar.register.mockClear(); + activate(context); + expect(statusBar.register).toHaveBeenCalled(); + }); it('should register an event handler to handle when the editor changes focus', () => { - activate(context) + activate(context); - expect(vscode.window.onDidChangeActiveTextEditor).toBeCalled() - expect(context.subscriptions.push.mock.calls[0]).toContain('onDidChangeActiveTextEditor') - }) + expect(vscode.window.onDidChangeActiveTextEditor).toBeCalled(); + expect(context.subscriptions.push.mock.calls[0]).toContain('onDidChangeActiveTextEditor'); + }); it('should register an event handler to handle when a document is saved', () => { - activate(context) + activate(context); - expect(vscode.workspace.onDidChangeTextDocument).toBeCalled() - expect(context.subscriptions.push.mock.calls[0]).toContain('onDidChangeTextDocument') - }) + expect(vscode.workspace.onDidChangeTextDocument).toBeCalled(); + expect(context.subscriptions.push.mock.calls[0]).toContain('onDidChangeTextDocument'); + }); it('should register an event handler to handle when an extension configuration changed', () => { - activate(context) + activate(context); - expect(vscode.workspace.onDidChangeConfiguration).toBeCalled() - expect(context.subscriptions.push.mock.calls[0]).toContain('onDidChangeConfiguration') - }) + expect(vscode.workspace.onDidChangeConfiguration).toBeCalled(); + expect(context.subscriptions.push.mock.calls[0]).toContain('onDidChangeConfiguration'); + }); it('should register an event handler to handle when workspace folders changed', () => { - activate(context) + activate(context); - expect(vscode.workspace.onDidChangeWorkspaceFolders).toBeCalled() - expect(context.subscriptions.push.mock.calls[0]).toContain('onDidChangeWorkspaceFolders') - }) + expect(vscode.workspace.onDidChangeWorkspaceFolders).toBeCalled(); + expect(context.subscriptions.push.mock.calls[0]).toContain('onDidChangeWorkspaceFolders'); + }); describe('should register a command', () => { beforeEach(() => { - jestInstance.toggleCoverageOverlay.mockReset() - jestInstance.runTest.mockReset() - jestInstance.startProcess.mockReset() - jestInstance.stopProcess.mockReset() - jestInstance.restartProcess.mockReset() - }) + jestInstance.toggleCoverageOverlay.mockReset(); + jestInstance.runTest.mockReset(); + jestInstance.startProcess.mockReset(); + jestInstance.stopProcess.mockReset(); + jestInstance.restartProcess.mockReset(); + }); it('to start extension', () => { - activate(context) + activate(context); const callArg = context.subscriptions.push.mock.calls[0].find((args) => { - return args[0] === `${extensionName}.start` - }) + return args[0] === `${extensionName}.start`; + }); - expect(callArg).toBeDefined() - callArg[1](jestInstance) - expect(jestInstance.startProcess).toHaveBeenCalled() - }) + expect(callArg).toBeDefined(); + callArg[1](jestInstance); + expect(jestInstance.startProcess).toHaveBeenCalled(); + }); it('to stop extension', () => { - activate(context) + activate(context); const callArg = context.subscriptions.push.mock.calls[0].find((args) => { - return args[0] === `${extensionName}.stop` - }) + return args[0] === `${extensionName}.stop`; + }); - expect(callArg).toBeDefined() - callArg[1](jestInstance) - expect(jestInstance.stopProcess).toHaveBeenCalled() - }) + expect(callArg).toBeDefined(); + callArg[1](jestInstance); + expect(jestInstance.stopProcess).toHaveBeenCalled(); + }); it('to restart extension', () => { - activate(context) + activate(context); const callArg = context.subscriptions.push.mock.calls[0].find((args) => { - return args[0] === `${extensionName}.restart` - }) + return args[0] === `${extensionName}.restart`; + }); - expect(callArg).toBeDefined() - callArg[1](jestInstance) - expect(jestInstance.restartProcess).toHaveBeenCalled() - }) + expect(callArg).toBeDefined(); + callArg[1](jestInstance); + expect(jestInstance.restartProcess).toHaveBeenCalled(); + }); it('to toggle the coverage overlay visibility', () => { - activate(context) + activate(context); const callArg = context.subscriptions.push.mock.calls[0].find((args) => { - return args[0] === `${extensionName}.coverage.toggle` - }) + return args[0] === `${extensionName}.coverage.toggle`; + }); - expect(callArg).toBeDefined() - callArg[1](jestInstance) - expect(jestInstance.toggleCoverageOverlay).toHaveBeenCalled() - }) + expect(callArg).toBeDefined(); + callArg[1](jestInstance); + expect(jestInstance.toggleCoverageOverlay).toHaveBeenCalled(); + }); it('to run specific test', () => { - activate(context) + activate(context); const callArg = context.subscriptions.push.mock.calls[0].find((args) => { - return args[0] === `${extensionName}.run-test` - }) + return args[0] === `${extensionName}.run-test`; + }); - expect(callArg).toBeDefined() - callArg[1]({ uri: '' }) - expect(jestInstance.runTest).toHaveBeenCalled() - }) - }) + expect(callArg).toBeDefined(); + callArg[1]({ uri: '' }); + expect(jestInstance.runTest).toHaveBeenCalled(); + }); + }); it('should register a DebugConfigurationProvider', () => { - const register = vscode.debug.registerDebugConfigurationProvider as jest.Mock<any> - register.mockReset() + const register = vscode.debug.registerDebugConfigurationProvider as jest.Mock<any>; + register.mockReset(); - activate(context) + activate(context); - expect(register).toHaveBeenCalledTimes(2) - const registeredAsNode = register.mock.calls.some((parameters) => parameters[0] === 'node') - const registeredAsJestTest = register.mock.calls.some((parameters) => parameters[0] === 'vscode-jest-tests') - expect(registeredAsNode && registeredAsJestTest).toBeTruthy() - }) - }) + expect(register).toHaveBeenCalledTimes(2); + const registeredAsNode = register.mock.calls.some((parameters) => parameters[0] === 'node'); + const registeredAsJestTest = register.mock.calls.some( + (parameters) => parameters[0] === 'vscode-jest-tests' + ); + expect(registeredAsNode && registeredAsJestTest).toBeTruthy(); + }); + }); describe('deactivate()', () => { it('should call unregisterAll on instancesManager', () => { - deactivate() - expect(extensionManager.unregisterAll).toBeCalled() - }) - }) -}) + deactivate(); + expect(extensionManager.unregisterAll).toBeCalled(); + }); + }); +}); diff --git a/tests/extensionManager.test.ts b/tests/extensionManager.test.ts index dc318e622..3fe8a040d 100644 --- a/tests/extensionManager.test.ts +++ b/tests/extensionManager.test.ts @@ -1,64 +1,68 @@ -jest.unmock('../src/extensionManager') +jest.unmock('../src/extensionManager'); const jestInstance = { deactivate: jest.fn(), onDidCloseTextDocument: jest.fn(), onDidChangeActiveTextEditor: jest.fn(), onDidChangeTextDocument: jest.fn(), -} +}; jest.mock('../src/JestExt', () => ({ JestExt: jest.fn().mockImplementation(() => jestInstance), -})) - -import * as vscode from 'vscode' -import { ExtensionManager, getExtensionResourceSettings, getExtensionWindowSettings } from '../src/extensionManager' -import { TestState } from '../src/DebugCodeLens' -import { readFileSync } from 'fs' -import { PluginWindowSettings } from '../src/Settings' +})); + +import * as vscode from 'vscode'; +import { + ExtensionManager, + getExtensionResourceSettings, + getExtensionWindowSettings, +} from '../src/extensionManager'; +import { TestState } from '../src/DebugCodeLens'; +import { readFileSync } from 'fs'; +import { PluginWindowSettings } from '../src/Settings'; vscode.workspace.getConfiguration = jest.fn().mockImplementation((section) => { - const data = readFileSync('./package.json') - const config = JSON.parse(data.toString()).contributes.configuration.properties + const data = readFileSync('./package.json'); + const config = JSON.parse(data.toString()).contributes.configuration.properties; - const defaults = {} + const defaults = {}; for (const key of Object.keys(config)) { if (section.length === 0 || key.startsWith(`${section}.`)) { - defaults[key] = config[key].default + defaults[key] = config[key].default; } } return { get: jest.fn().mockImplementation((key) => defaults[`${section}.${key}`]), - } -}) + }; +}); describe('InstancesManager', () => { - let extensionManager: ExtensionManager + let extensionManager: ExtensionManager; const registerInstance = (folderName: string) => { - extensionManager.register({ name: folderName, uri: { fsPath: folderName } } as any) - } - const registerSpy = jest.spyOn(ExtensionManager.prototype, 'register') - const unregisterSpy = jest.spyOn(ExtensionManager.prototype, 'unregister') + extensionManager.register({ name: folderName, uri: { fsPath: folderName } } as any); + }; + const registerSpy = jest.spyOn(ExtensionManager.prototype, 'register'); + const unregisterSpy = jest.spyOn(ExtensionManager.prototype, 'unregister'); beforeEach(() => { - extensionManager = new ExtensionManager({} as any) - registerSpy.mockClear() - unregisterSpy.mockClear() - ;(vscode.workspace as any).workspaceFolders = [ + extensionManager = new ExtensionManager({} as any); + registerSpy.mockClear(); + unregisterSpy.mockClear(); + (vscode.workspace as any).workspaceFolders = [ { uri: { fsPath: 'workspaceFolder1' }, name: 'workspaceFolder1' }, - ] as any - ;(jestInstance.deactivate as any).mockReset() - ;(vscode.window.showWorkspaceFolderPick as any).mockReset() - ;(vscode.commands.registerCommand as any).mockReset() - }) + ] as any; + (jestInstance.deactivate as any).mockReset(); + (vscode.window.showWorkspaceFolderPick as any).mockReset(); + (vscode.commands.registerCommand as any).mockReset(); + }); describe('constructor()', () => { it('should register extensions for all wrokspace folders', () => { - new ExtensionManager({} as any) - expect(registerSpy).toHaveBeenCalledTimes(1) - }) - }) + new ExtensionManager({} as any); + expect(registerSpy).toHaveBeenCalledTimes(1); + }); + }); describe('applySettings()', () => { it('should save settings to instance', () => { @@ -68,10 +72,10 @@ describe('InstancesManager', () => { showWhenTestStateIn: [], }, disabledWorkspaceFolders: [], - } - extensionManager.applySettings(newSettings) - expect((extensionManager as any).commonPluginSettings).toEqual(newSettings) - }) + }; + extensionManager.applySettings(newSettings); + expect((extensionManager as any).commonPluginSettings).toEqual(newSettings); + }); it('should update debugCodeLensProvider instance', () => { const newSettings: PluginWindowSettings = { debugCodeLens: { @@ -79,262 +83,278 @@ describe('InstancesManager', () => { showWhenTestStateIn: [TestState.Fail], }, disabledWorkspaceFolders: ['workspaceFolder1'], - } - extensionManager.applySettings(newSettings) + }; + extensionManager.applySettings(newSettings); expect((extensionManager as any).debugCodeLensProvider.showWhenTestStateIn).toEqual( newSettings.debugCodeLens.showWhenTestStateIn - ) - }) + ); + }); it('should respect disabledWorkspaceFolders', () => { - registerInstance('workspaceFolder1') - registerInstance('workspaceFolder2') + registerInstance('workspaceFolder1'); + registerInstance('workspaceFolder2'); - expect(extensionManager.getByName('workspaceFolder1')).toBeDefined() - expect(extensionManager.getByName('workspaceFolder2')).toBeDefined() + expect(extensionManager.getByName('workspaceFolder1')).toBeDefined(); + expect(extensionManager.getByName('workspaceFolder2')).toBeDefined(); const newSettings: PluginWindowSettings = { debugCodeLens: { enabled: true, showWhenTestStateIn: [], }, disabledWorkspaceFolders: ['workspaceFolder1'], - } - extensionManager.applySettings(newSettings) - expect(extensionManager.getByName('workspaceFolder1')).toBeUndefined() - expect(extensionManager.getByName('workspaceFolder2')).toBeDefined() - }) - }) + }; + extensionManager.applySettings(newSettings); + expect(extensionManager.getByName('workspaceFolder1')).toBeUndefined(); + expect(extensionManager.getByName('workspaceFolder2')).toBeDefined(); + }); + }); describe('register()', () => { it('should register an instance', () => { - registerInstance('workspaceFolder1') - expect(extensionManager.getByName('workspaceFolder1')).toBe(jestInstance) - }) - }) + registerInstance('workspaceFolder1'); + expect(extensionManager.getByName('workspaceFolder1')).toBe(jestInstance); + }); + }); describe('unregister()', () => { it('should unregister instance by wokspaceFolder', () => { - registerInstance('workspaceFolder1') - extensionManager.unregister({ name: 'workspaceFolder1' } as any) - expect(extensionManager.getByName('workspaceFolder1')).toBeUndefined() - expect(jestInstance.deactivate).toHaveBeenCalled() - }) - }) + registerInstance('workspaceFolder1'); + extensionManager.unregister({ name: 'workspaceFolder1' } as any); + expect(extensionManager.getByName('workspaceFolder1')).toBeUndefined(); + expect(jestInstance.deactivate).toHaveBeenCalled(); + }); + }); describe('unregisterByName()', () => { it('should unregister instance by wokspaceFolder name', () => { - registerInstance('workspaceFolder1') - extensionManager.unregisterByName('workspaceFolder1') - expect(extensionManager.getByName('workspaceFolder1')).toBeUndefined() - expect(jestInstance.deactivate).toHaveBeenCalled() - }) - }) + registerInstance('workspaceFolder1'); + extensionManager.unregisterByName('workspaceFolder1'); + expect(extensionManager.getByName('workspaceFolder1')).toBeUndefined(); + expect(jestInstance.deactivate).toHaveBeenCalled(); + }); + }); describe('unregisterAll()', () => { it('should unregister all instances', () => { - registerInstance('workspaceFolder1') - registerInstance('workspaceFolder2') - extensionManager.unregisterAll() - expect(extensionManager.getByName('workspaceFolder1')).toBeUndefined() - expect(extensionManager.getByName('workspaceFolder2')).toBeUndefined() - expect(jestInstance.deactivate).toHaveBeenCalledTimes(2) - }) - }) + registerInstance('workspaceFolder1'); + registerInstance('workspaceFolder2'); + extensionManager.unregisterAll(); + expect(extensionManager.getByName('workspaceFolder1')).toBeUndefined(); + expect(extensionManager.getByName('workspaceFolder2')).toBeUndefined(); + expect(jestInstance.deactivate).toHaveBeenCalledTimes(2); + }); + }); describe('shouldStart()', () => { it('should check whether instance already started', () => { - registerInstance('workspaceFolder1') - expect(extensionManager.shouldStart('workspaceFolder1')).toEqual(false) - expect(extensionManager.shouldStart('workspaceFolder2')).toEqual(true) - }) + registerInstance('workspaceFolder1'); + expect(extensionManager.shouldStart('workspaceFolder1')).toEqual(false); + expect(extensionManager.shouldStart('workspaceFolder2')).toEqual(true); + }); it('should check if folder is in disabledFolderNames', () => { - ;(extensionManager as any).commonPluginSettings.disabledWorkspaceFolders = ['workspaceFolder2'] - expect(extensionManager.shouldStart('workspaceFolder2')).toEqual(false) - expect(extensionManager.shouldStart('workspaceFolder3')).toEqual(true) - }) - }) + (extensionManager as any).commonPluginSettings.disabledWorkspaceFolders = [ + 'workspaceFolder2', + ]; + expect(extensionManager.shouldStart('workspaceFolder2')).toEqual(false); + expect(extensionManager.shouldStart('workspaceFolder3')).toEqual(true); + }); + }); describe('getByName()', () => { it('should return extension', () => { - registerInstance('workspaceFolder1') - expect(extensionManager.getByName('workspaceFolder1')).toBe(jestInstance) - expect(extensionManager.getByName('workspaceFolder2')).toBeUndefined() - }) - }) + registerInstance('workspaceFolder1'); + expect(extensionManager.getByName('workspaceFolder1')).toBe(jestInstance); + expect(extensionManager.getByName('workspaceFolder2')).toBeUndefined(); + }); + }); describe('getByDocUri()', () => { it('should return extension', async () => { - registerInstance('workspaceFolder1') - ;(vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce({ name: 'workspaceFolder1' }) - expect(extensionManager.getByDocUri(null)).toBe(jestInstance) - }) + registerInstance('workspaceFolder1'); + (vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce({ + name: 'workspaceFolder1', + }); + expect(extensionManager.getByDocUri(null)).toBe(jestInstance); + }); it('should return undefined if no workspace found for uri', () => { - ;(vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce(undefined) - expect(extensionManager.getByDocUri(null)).toBeUndefined() - }) - }) + (vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce(undefined); + expect(extensionManager.getByDocUri(null)).toBeUndefined(); + }); + }); describe('get()', () => { afterEach(() => { - ;(vscode.workspace as any).workspaceFolders = [ + (vscode.workspace as any).workspaceFolders = [ { uri: { fsPath: 'workspaceFolder1' }, name: 'workspaceFolder1' }, - ] as any - }) + ] as any; + }); it('should return extension at once if there is only one workspace folder', async () => { - registerInstance('workspaceFolder1') - expect(await extensionManager.get()).toBe(jestInstance) - }) + registerInstance('workspaceFolder1'); + expect(await extensionManager.get()).toBe(jestInstance); + }); it('should prompt for workspace if there are more then one workspace folder', async () => { - registerInstance('workspaceFolder1') - ;(vscode.workspace as any).workspaceFolders = [{ name: 'workspaceFolder1' }, { name: 'workspaceFolder2' }] as any - ;(vscode.window.showWorkspaceFolderPick as any).mockReturnValue({ name: 'workspaceFolder1' }) - expect(await extensionManager.get()).toBe(jestInstance) - expect(vscode.window.showWorkspaceFolderPick).toHaveBeenCalled() - }) + registerInstance('workspaceFolder1'); + (vscode.workspace as any).workspaceFolders = [ + { name: 'workspaceFolder1' }, + { name: 'workspaceFolder2' }, + ] as any; + (vscode.window.showWorkspaceFolderPick as any).mockReturnValue({ name: 'workspaceFolder1' }); + expect(await extensionManager.get()).toBe(jestInstance); + expect(vscode.window.showWorkspaceFolderPick).toHaveBeenCalled(); + }); it('should return undefined if no workspace selected', async () => { - ;(vscode.workspace as any).workspaceFolders = [{ name: 'workspaceFolder1' }, { name: 'workspaceFolder2' }] as any - ;(vscode.window.showWorkspaceFolderPick as any).mockReturnValue(undefined) - expect(await extensionManager.get()).toBeUndefined() - }) + (vscode.workspace as any).workspaceFolders = [ + { name: 'workspaceFolder1' }, + { name: 'workspaceFolder2' }, + ] as any; + (vscode.window.showWorkspaceFolderPick as any).mockReturnValue(undefined); + expect(await extensionManager.get()).toBeUndefined(); + }); it('should throw if no jest instance found for workspace', async () => { - extensionManager.getByName = jest.fn().mockReturnValue(undefined) - let error + extensionManager.getByName = jest.fn().mockReturnValue(undefined); + let error; try { - await extensionManager.get() + await extensionManager.get(); } catch (e) { - error = e + error = e; } - expect(error).toBeDefined() - }) - }) + expect(error).toBeDefined(); + }); + }); describe('registerCommand()', () => { it('should register command and preserve context', async () => { - const command = 'command' - const thisArg = {} + const command = 'command'; + const thisArg = {}; const callback = jest.fn(function () { - expect(this).toBe(thisArg) - }) - ;(vscode.commands.registerCommand as any).mockImplementation((_command, _callback) => { - expect(_command).toBe(command) - expect(_callback).toBeDefined() - return _callback - }) - const callbackWrap = extensionManager.registerCommand(command, callback, thisArg) as any - await callbackWrap() - expect(callback).toHaveBeenCalled() - expect(vscode.commands.registerCommand).toHaveBeenCalled() - }) + expect(this).toBe(thisArg); + }); + (vscode.commands.registerCommand as any).mockImplementation((_command, _callback) => { + expect(_command).toBe(command); + expect(_callback).toBeDefined(); + return _callback; + }); + const callbackWrap = extensionManager.registerCommand(command, callback, thisArg) as any; + await callbackWrap(); + expect(callback).toHaveBeenCalled(); + expect(vscode.commands.registerCommand).toHaveBeenCalled(); + }); it('should pass jest instance before other arguments to callback', async () => { - registerInstance('workspaceFolder1') - const arg0 = 'arg0' - const callback = jest.fn() - ;(vscode.commands.registerCommand as any).mockImplementation(async (_command, _callback) => { - await _callback(arg0) - }) - await extensionManager.registerCommand('command', callback) - expect(callback).toHaveBeenCalledWith(jestInstance, arg0) - }) - }) + registerInstance('workspaceFolder1'); + const arg0 = 'arg0'; + const callback = jest.fn(); + (vscode.commands.registerCommand as any).mockImplementation(async (_command, _callback) => { + await _callback(arg0); + }); + await extensionManager.registerCommand('command', callback); + expect(callback).toHaveBeenCalledWith(jestInstance, arg0); + }); + }); describe('onDidChangeConfiguration()', () => { it('checks if changes affects jest', () => { const arg = { affectsConfiguration: jest.fn(), - } - extensionManager.onDidChangeConfiguration(arg) - expect(arg.affectsConfiguration).toHaveBeenCalled() - }) + }; + extensionManager.onDidChangeConfiguration(arg); + expect(arg.affectsConfiguration).toHaveBeenCalled(); + }); it('checks configuration for every workspace', () => { const arg = { affectsConfiguration: jest.fn(), - } - extensionManager.onDidChangeConfiguration(arg) + }; + extensionManager.onDidChangeConfiguration(arg); // 1 for window settings + 1 for workspace folder - expect(arg.affectsConfiguration).toHaveBeenCalledTimes(2) - }) - }) + expect(arg.affectsConfiguration).toHaveBeenCalledTimes(2); + }); + }); describe('onDidChangeWorkspaceFolders()', () => { it('should register all new folders', () => { extensionManager.onDidChangeWorkspaceFolders({ added: [{ name: 'wokspaceFolderAdded', uri: { fsPath: 'wokspaceFolderAdded' } }], removed: [], - } as any) - expect(registerSpy).toHaveBeenCalledTimes(1) - }) + } as any); + expect(registerSpy).toHaveBeenCalledTimes(1); + }); it('should unregister all removed folders', () => { - registerInstance('wokspaceFolderAdded') + registerInstance('wokspaceFolderAdded'); extensionManager.onDidChangeWorkspaceFolders({ added: [], removed: [{ name: 'wokspaceFolderAdded', uri: { fsPath: 'wokspaceFolderAdded' } }], - } as any) - expect(unregisterSpy).toHaveBeenCalledTimes(1) - }) - }) + } as any); + expect(unregisterSpy).toHaveBeenCalledTimes(1); + }); + }); describe('onDidCloseTextDocument()', () => { afterEach(() => { - jestInstance.onDidCloseTextDocument.mockClear() - }) + jestInstance.onDidCloseTextDocument.mockClear(); + }); it('should call extension method', () => { - ;(vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce({ name: 'workspaceFolder1' }) - extensionManager.onDidCloseTextDocument({} as any) - expect(jestInstance.onDidCloseTextDocument).toHaveBeenCalled() - }) + (vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce({ + name: 'workspaceFolder1', + }); + extensionManager.onDidCloseTextDocument({} as any); + expect(jestInstance.onDidCloseTextDocument).toHaveBeenCalled(); + }); it('should not call try to call extension method if no extension', () => { - ;(vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce(undefined) - extensionManager.onDidCloseTextDocument({} as any) - expect(jestInstance.onDidCloseTextDocument).not.toHaveBeenCalled() - }) - }) + (vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce(undefined); + extensionManager.onDidCloseTextDocument({} as any); + expect(jestInstance.onDidCloseTextDocument).not.toHaveBeenCalled(); + }); + }); describe('onDidChangeActiveTextEditor()', () => { afterEach(() => { - jestInstance.onDidChangeActiveTextEditor.mockClear() - }) + jestInstance.onDidChangeActiveTextEditor.mockClear(); + }); it('should call extension method', () => { - ;(vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce({ name: 'workspaceFolder1' }) - extensionManager.onDidChangeActiveTextEditor({ document: {} } as any) - expect(jestInstance.onDidChangeActiveTextEditor).toHaveBeenCalled() - }) + (vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce({ + name: 'workspaceFolder1', + }); + extensionManager.onDidChangeActiveTextEditor({ document: {} } as any); + expect(jestInstance.onDidChangeActiveTextEditor).toHaveBeenCalled(); + }); it('should not call try to call extension method if no document', () => { - extensionManager.onDidChangeActiveTextEditor({} as any) - expect(jestInstance.onDidChangeActiveTextEditor).not.toHaveBeenCalled() - }) + extensionManager.onDidChangeActiveTextEditor({} as any); + expect(jestInstance.onDidChangeActiveTextEditor).not.toHaveBeenCalled(); + }); it('should not call try to call extension method if no extension', () => { - ;(vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce(undefined) - extensionManager.onDidChangeActiveTextEditor({ document: {} } as any) - expect(jestInstance.onDidChangeActiveTextEditor).not.toHaveBeenCalled() - }) - }) + (vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce(undefined); + extensionManager.onDidChangeActiveTextEditor({ document: {} } as any); + expect(jestInstance.onDidChangeActiveTextEditor).not.toHaveBeenCalled(); + }); + }); describe('onDidChangeTextDocument()', () => { afterEach(() => { - jestInstance.onDidChangeTextDocument.mockClear() - }) + jestInstance.onDidChangeTextDocument.mockClear(); + }); it('should call extension method', () => { - ;(vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce({ name: 'workspaceFolder1' }) - extensionManager.onDidChangeTextDocument({ document: {} } as any) - expect(jestInstance.onDidChangeTextDocument).toHaveBeenCalled() - }) + (vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce({ + name: 'workspaceFolder1', + }); + extensionManager.onDidChangeTextDocument({ document: {} } as any); + expect(jestInstance.onDidChangeTextDocument).toHaveBeenCalled(); + }); it('should not call try to call extension method if no extension', () => { - ;(vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce(undefined) - extensionManager.onDidChangeTextDocument({ document: {} } as any) - expect(jestInstance.onDidChangeTextDocument).not.toHaveBeenCalled() - }) - }) + (vscode.workspace.getWorkspaceFolder as any).mockReturnValueOnce(undefined); + extensionManager.onDidChangeTextDocument({ document: {} } as any); + expect(jestInstance.onDidChangeTextDocument).not.toHaveBeenCalled(); + }); + }); describe('getExtensionResourceSettings()', () => { it('should return the extension resource configuration', async () => { @@ -350,9 +370,9 @@ describe('InstancesManager', () => { runAllTestsFirst: true, showCoverageOnLoad: false, debugMode: false, - }) - }) - }) + }); + }); + }); describe('getExtensionWindowSettings()', () => { it('should return the extension window configuration', async () => { @@ -363,7 +383,7 @@ describe('InstancesManager', () => { }, enableSnapshotPreviews: true, disabledWorkspaceFolders: [], - }) - }) - }) -}) + }); + }); + }); +}); diff --git a/tests/helpers.test.ts b/tests/helpers.test.ts index c04a65707..9d6f7b429 100644 --- a/tests/helpers.test.ts +++ b/tests/helpers.test.ts @@ -1,74 +1,81 @@ -jest.unmock('../src/helpers') -jest.unmock('../src/Settings') +jest.unmock('../src/helpers'); +jest.unmock('../src/Settings'); -const mockExistsSync = jest.fn() -const mockReadFileSync = jest.fn() +const mockExistsSync = jest.fn(); +const mockReadFileSync = jest.fn(); jest.mock('fs', () => ({ existsSync: mockExistsSync, readFileSync: mockReadFileSync, -})) +})); -const mockPlatform = jest.fn() -jest.mock('os', () => ({ platform: mockPlatform })) +const mockPlatform = jest.fn(); +jest.mock('os', () => ({ platform: mockPlatform })); -const mockJoin = jest.fn() -const mockNormalize = jest.fn() +const mockJoin = jest.fn(); +const mockNormalize = jest.fn(); jest.mock('path', () => ({ join: mockJoin, normalize: mockNormalize, -})) +})); -import { isCreateReactAppTestCommand, pathToJest, nodeBinExtension, cleanAnsi } from '../src/helpers' +import { + isCreateReactAppTestCommand, + pathToJest, + nodeBinExtension, + cleanAnsi, +} from '../src/helpers'; // Manually (forcefully) set the executable's file extension to test its addition independendly of the operating system. -;(nodeBinExtension as string) = '.TEST' +(nodeBinExtension as string) = '.TEST'; describe('ModuleHelpers', () => { describe('nodeBinExtension', () => { // Since `nodeBinExtension` is a variable, we have to reload the module in order to re-evaluate it. it('should return an empty string on Linux', () => { - jest.resetModules() - mockPlatform.mockReturnValueOnce('linux') - expect(require('../src/helpers').nodeBinExtension).toBe('') - }) + jest.resetModules(); + mockPlatform.mockReturnValueOnce('linux'); + expect(require('../src/helpers').nodeBinExtension).toBe(''); + }); it('should equal ".cmd" on Windows', () => { - jest.resetModules() - mockPlatform.mockReturnValueOnce('win32') - expect(require('../src/helpers').nodeBinExtension).toBe('.cmd') - }) - }) + jest.resetModules(); + mockPlatform.mockReturnValueOnce('win32'); + expect(require('../src/helpers').nodeBinExtension).toBe('.cmd'); + }); + }); describe('isCreateReactAppTestCommand', () => { it('should return true for CRA', () => { - expect(isCreateReactAppTestCommand('react-scripts test --env=jsdom')).toBe(true) - }) + expect(isCreateReactAppTestCommand('react-scripts test --env=jsdom')).toBe(true); + }); it('should return true for CRA with cross-env', () => { - expect(isCreateReactAppTestCommand('cross-env CI=true react-scripts test --env=jsdom')).toBe(true) - }) + expect(isCreateReactAppTestCommand('cross-env CI=true react-scripts test --env=jsdom')).toBe( + true + ); + }); it('should return false for other scripts', () => { - expect(isCreateReactAppTestCommand(undefined)).toBe(false) - expect(isCreateReactAppTestCommand('custom-script')).toBe(false) - }) + expect(isCreateReactAppTestCommand(undefined)).toBe(false); + expect(isCreateReactAppTestCommand('custom-script')).toBe(false); + }); it('should return false for other scripts with cross-env', () => { - expect(isCreateReactAppTestCommand('cross-env CI=true custom-script')).toBe(false) - }) - }) + expect(isCreateReactAppTestCommand('cross-env CI=true custom-script')).toBe(false); + }); + }); describe('pathToJest', () => { const defaultSettings: any = { pathToJest: null, rootPath: '', - } + }; beforeEach(() => { - mockJoin.mockImplementation(require.requireActual('path').join) - mockNormalize.mockImplementation(require.requireActual('path').normalize) - mockExistsSync.mockImplementation(require.requireActual('path').existsSync) - }) + mockJoin.mockImplementation(require.requireActual('path').join); + mockNormalize.mockImplementation(require.requireActual('path').normalize); + mockExistsSync.mockImplementation(require.requireActual('path').existsSync); + }); it('returns "npm test --" when bootstrapped with create-react-app', () => { mockReadFileSync.mockReturnValueOnce( @@ -77,35 +84,35 @@ describe('ModuleHelpers', () => { test: 'react-scripts test', }, }) - ) + ); - expect(pathToJest(defaultSettings)).toBe('npm test --') - }) + expect(pathToJest(defaultSettings)).toBe('npm test --'); + }); it('returns the normalized "pathToJest" setting when set by the user', () => { - const expected = {} - mockNormalize.mockReturnValueOnce(expected) + const expected = {}; + mockNormalize.mockReturnValueOnce(expected); const settings: any = { pathToJest: expected, rootPath: '', - } + }; - expect(pathToJest(settings)).toBe(expected) - expect(mockNormalize).toBeCalledWith(settings.pathToJest) - }) + expect(pathToJest(settings)).toBe(expected); + expect(mockNormalize).toBeCalledWith(settings.pathToJest); + }); it('defaults to "node_modules/.bin/jest" when Jest is locally installed', () => { - const expected = 'node_modules/.bin/jest.TEST' + const expected = 'node_modules/.bin/jest.TEST'; - mockJoin.mockImplementation(require.requireActual('path').posix.join) - mockNormalize.mockImplementation((arg) => arg) - mockExistsSync.mockImplementation((path) => path === expected) + mockJoin.mockImplementation(require.requireActual('path').posix.join); + mockNormalize.mockImplementation((arg) => arg); + mockExistsSync.mockImplementation((path) => path === expected); - expect(pathToJest(defaultSettings)).toBe(`"${expected}"`) - }) + expect(pathToJest(defaultSettings)).toBe(`"${expected}"`); + }); it('default jestToPath path can preserve special characters', () => { - mockJoin.mockImplementation(require.requireActual('path').posix.join) - mockNormalize.mockImplementation((arg) => arg) + mockJoin.mockImplementation(require.requireActual('path').posix.join); + mockNormalize.mockImplementation((arg) => arg); const testPaths = [ '/root/my dir/space', @@ -113,31 +120,33 @@ describe('ModuleHelpers', () => { '/root/👍/emoji', '/root/外國人/unicode', '/root/\\space/double-escape', - ] + ]; testPaths.forEach((p) => { - const settings = { ...defaultSettings, rootPath: p } - const expected = `${p}/node_modules/.bin/jest.TEST` - mockExistsSync.mockImplementation((path) => path === expected) - expect(pathToJest(settings)).toBe(`"${expected}"`) - }) - }) + const settings = { ...defaultSettings, rootPath: p }; + const expected = `${p}/node_modules/.bin/jest.TEST`; + mockExistsSync.mockImplementation((path) => path === expected); + expect(pathToJest(settings)).toBe(`"${expected}"`); + }); + }); it('defaults to "jest" when Jest is not locally installed', () => { - const expected = '"jest.TEST"' + const expected = '"jest.TEST"'; - mockJoin.mockImplementation(require.requireActual('path').posix.join) - mockNormalize.mockImplementation((arg) => arg) - mockExistsSync.mockImplementation(() => false) + mockJoin.mockImplementation(require.requireActual('path').posix.join); + mockNormalize.mockImplementation((arg) => arg); + mockExistsSync.mockImplementation(() => false); - expect(pathToJest(defaultSettings)).toBe(expected) - }) - }) + expect(pathToJest(defaultSettings)).toBe(expected); + }); + }); describe('cleanAnsi', () => { it('removes ANSI characters from string', () => { const ansiString = - '\u001b[36m<body>\u001b[39m \u001b[36m<div>\u001b[39m \u001b[36m<div\u001b[39m \u001b[33mclass\u001b[39m=\u001b[32m"root"\u001b[39m \u001b[36m>\u001b[39m \u001b[0mLearn React\u001b[0m \u001b[36m</div>\u001b[39m \u001b[36m</div>\u001b[39m\u001b[36m</body>\u001b[39m' - - expect(cleanAnsi(ansiString)).toBe('<body> <div> <div class="root" > Learn React </div> </div></body>') - }) - }) -}) + '\u001b[36m<body>\u001b[39m \u001b[36m<div>\u001b[39m \u001b[36m<div\u001b[39m \u001b[33mclass\u001b[39m=\u001b[32m"root"\u001b[39m \u001b[36m>\u001b[39m \u001b[0mLearn React\u001b[0m \u001b[36m</div>\u001b[39m \u001b[36m</div>\u001b[39m\u001b[36m</body>\u001b[39m'; + + expect(cleanAnsi(ansiString)).toBe( + '<body> <div> <div class="root" > Learn React </div> </div></body>' + ); + }); + }); +}); diff --git a/tests/messaging.test.ts b/tests/messaging.test.ts index e760af586..2202bad2b 100644 --- a/tests/messaging.test.ts +++ b/tests/messaging.test.ts @@ -1,80 +1,80 @@ -jest.unmock('events') -jest.unmock('../src/messaging') +jest.unmock('events'); +jest.unmock('../src/messaging'); -import * as messaging from '../src/messaging' -import { window, commands, Uri } from 'vscode' +import * as messaging from '../src/messaging'; +import { window, commands, Uri } from 'vscode'; describe('test system messaging', () => { - const mockShowErrorMessage = window.showErrorMessage as jest.Mock<any> - const mockShowWarningMessage = window.showWarningMessage as jest.Mock<any> - const mockExecCommands = commands.executeCommand as jest.Mock<any> - const mockUriParse = Uri.parse as jest.Mock<any> + const mockShowErrorMessage = window.showErrorMessage as jest.Mock<any>; + const mockShowWarningMessage = window.showWarningMessage as jest.Mock<any>; + const mockExecCommands = commands.executeCommand as jest.Mock<any>; + const mockUriParse = Uri.parse as jest.Mock<any>; const thenable = () => { /* do nothing */ - } + }; beforeEach(() => { - jest.resetAllMocks() + jest.resetAllMocks(); - mockShowErrorMessage.mockReturnValue({ then: thenable }) - mockShowWarningMessage.mockReturnValue({ then: thenable }) - }) + mockShowErrorMessage.mockReturnValue({ then: thenable }); + mockShowWarningMessage.mockReturnValue({ then: thenable }); + }); it('can show system message without actions', () => { const validate = (mockF: jest.Mock<any>) => { - expect(mockF.mock.calls.length).toBe(1) - const args = mockF.mock.calls[0] - expect(args.length).toBe(1) - expect(args[0]).toBe('an error') - } + expect(mockF.mock.calls.length).toBe(1); + const args = mockF.mock.calls[0]; + expect(args.length).toBe(1); + expect(args[0]).toBe('an error'); + }; - messaging.systemWarningMessage('an error') - validate(mockShowWarningMessage) + messaging.systemWarningMessage('an error'); + validate(mockShowWarningMessage); - messaging.systemErrorMessage('an error') - validate(mockShowErrorMessage) - }) + messaging.systemErrorMessage('an error'); + validate(mockShowErrorMessage); + }); it('can show system message with actions', () => { - const action1: messaging.MessageAction = { title: 'action1', action: () => {} } - const action2: messaging.MessageAction = { title: 'action2', action: () => {} } + const action1: messaging.MessageAction = { title: 'action1', action: () => {} }; + const action2: messaging.MessageAction = { title: 'action2', action: () => {} }; const validate = (mockF: jest.Mock<any>) => { - expect(mockF.mock.calls.length).toBe(1) - const args = mockF.mock.calls[0] - expect(args.length).toBe(3) - expect(args[0]).toBe('an error') - expect(args[1]).toBe('action1') - expect(args[2]).toBe('action2') - } + expect(mockF.mock.calls.length).toBe(1); + const args = mockF.mock.calls[0]; + expect(args.length).toBe(3); + expect(args[0]).toBe('an error'); + expect(args[1]).toBe('action1'); + expect(args[2]).toBe('action2'); + }; - messaging.systemWarningMessage('an error', action1, action2) - validate(mockShowWarningMessage) + messaging.systemWarningMessage('an error', action1, action2); + validate(mockShowWarningMessage); - messaging.systemErrorMessage('an error', action1, action2) - validate(mockShowErrorMessage) - }) + messaging.systemErrorMessage('an error', action1, action2); + validate(mockShowErrorMessage); + }); it('can open troubleshooting url via action', () => { - messaging.showTroubleshootingAction.action() - expect(mockExecCommands.mock.calls.length).toBe(1) - expect(mockUriParse.mock.calls[0][0]).toBe(messaging.TROUBLESHOOTING_URL) - }) + messaging.showTroubleshootingAction.action(); + expect(mockExecCommands.mock.calls.length).toBe(1); + expect(mockUriParse.mock.calls[0][0]).toBe(messaging.TROUBLESHOOTING_URL); + }); it('can handle user actions', () => { - let a = messaging._handleMessageActions() - expect(a).not.toBeNull() - a() + let a = messaging._handleMessageActions(); + expect(a).not.toBeNull(); + a(); - let done = false + let done = false; const action: messaging.MessageAction = { title: 'action1', action: () => { - done = true + done = true; }, - } - a = messaging._handleMessageActions([action]) - a() - expect(done).toBe(false) + }; + a = messaging._handleMessageActions([action]); + a(); + expect(done).toBe(false); - a('action1') - expect(done).toBe(true) - }) -}) + a('action1'); + expect(done).toBe(true); + }); +}); From 36e371b1cfe9d16c27abb0ade9f6dfb75c21d662 Mon Sep 17 00:00:00 2001 From: connectdotz <vsun@connectdotz.com> Date: Sun, 24 May 2020 21:40:48 -0400 Subject: [PATCH 11/11] fix the .js file format as well --- .eslintrc.js | 11 ++++++++++- jest.config.js | 22 +++++++++++----------- package.json | 4 ++-- prettier.config.js | 2 +- webpack.config.js | 16 ++++++++-------- 5 files changed, 32 insertions(+), 23 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 16f919d4e..072503071 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,4 +18,13 @@ module.exports = { rules: { 'prettier/prettier': 'error', }, -} + overrides: [ + { + files: ['*.js'], + rules: { + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + }, + }, + ], +}; diff --git a/jest.config.js b/jest.config.js index 4c083f8b4..c5050e7ab 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,17 +1,17 @@ module.exports = { preset: 'ts-jest', testEnvironment: 'node', - testRegex: "tests/.*\\.ts$", + testRegex: 'tests/.*\\.ts$', automock: true, - moduleFileExtensions: ["ts", "js", "json"], + moduleFileExtensions: ['ts', 'js', 'json'], unmockedModulePathPatterns: [ - "jest-editor-support/node_modules", - "color-convert", - "chalk", - "snapdragon", - "ansi-styles", - "core-js", - "debug", - "@babel/template", + 'jest-editor-support/node_modules', + 'color-convert', + 'chalk', + 'snapdragon', + 'ansi-styles', + 'core-js', + 'debug', + '@babel/template', ], -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index 79250f062..62e04fe65 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-jest", "displayName": "Jest", "description": "Use Facebook's Jest With Pleasure.", - "version": "3.2.0", + "version": "4.0.0-beta.0", "publisher": "Orta", "engines": { "vscode": "^1.23.0" @@ -272,7 +272,7 @@ "vscode:prepublish": "yarn clean-out && yarn compile", "compile": "webpack --mode production", "watch": "webpack --mode development --watch --info-verbosity verbose", - "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"*.json\"", + "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\" \"*.json\" \"*.js\" ", "test": "jest", "watch-test": "yarn test -- --watch" }, diff --git a/prettier.config.js b/prettier.config.js index 5092d77c3..4044259ae 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -11,4 +11,4 @@ module.exports = { }, }, ], -} +}; diff --git a/webpack.config.js b/webpack.config.js index 8fb6975c4..24f866381 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,23 +1,23 @@ //@ts-check -'use strict' +'use strict'; -const path = require('path') +const path = require('path'); /**@returns {import('webpack').Configuration}*/ module.exports = (env, argv) => { - const isProduction = argv.mode === 'production' - const isDevelopment = !isProduction + const isProduction = argv.mode === 'production'; + const isDevelopment = !isProduction; /**@type {any} */ const externals = [ { 'jest-config': 'root {}' }, // the jest-config module isn't utilized in this plugin, compiling it would result in unnecessary overhead and errors { vscode: 'commonjs vscode' }, // the vscode-module is created on-the-fly and must be excluded. - ] + ]; // during development keep the largest external dependencies out of the bundle in order to speed up build time if (isDevelopment) { - externals.push('typescript', /^\@babel\/.*/, 'babylon') + externals.push('typescript', /^@babel\/.*/, 'babylon'); } return { context: __dirname, @@ -46,5 +46,5 @@ module.exports = (env, argv) => { }, ], }, - } -} + }; +};