diff --git a/src/commands.ts b/src/commands.ts index 3275f5ee..a6474a68 100644 --- a/src/commands.ts +++ b/src/commands.ts @@ -8,7 +8,7 @@ import { CodeReviewData, CodeReviews, ExtensionState } from './extensionState'; import { GitGraphView } from './gitGraphView'; import { Logger } from './logger'; import { RepoManager } from './repoManager'; -import { GitExecutable, UNABLE_TO_FIND_GIT_MSG, abbrevCommit, abbrevText, copyToClipboard, doesVersionMeetRequirement, getExtensionVersion, getPathFromUri, getRelativeTimeDiff, getRepoName, getSortedRepositoryPaths, isPathInWorkspace, openFile, resolveToSymbolicPath, showErrorMessage, showInformationMessage } from './utils'; +import { GitExecutable, UNABLE_TO_FIND_GIT_MSG, VsCodeVersionRequirement, abbrevCommit, abbrevText, copyToClipboard, doesVersionMeetRequirement, getExtensionVersion, getPathFromUri, getRelativeTimeDiff, getRepoName, getSortedRepositoryPaths, isPathInWorkspace, openFile, resolveToSymbolicPath, showErrorMessage, showInformationMessage } from './utils'; import { Disposable } from './utils/disposable'; import { Event } from './utils/event'; @@ -65,7 +65,7 @@ export class CommandManager extends Disposable { // Register Extension Contexts try { - this.registerContext('git-graph:codiconsSupported', doesVersionMeetRequirement(vscode.version, '1.42.0')); + this.registerContext('git-graph:codiconsSupported', doesVersionMeetRequirement(vscode.version, VsCodeVersionRequirement.Codicons)); } catch (_) { this.logger.logError('Unable to set Visual Studio Code Context "git-graph:codiconsSupported"'); } diff --git a/src/dataSource.ts b/src/dataSource.ts index 7e210db2..cc3028a7 100644 --- a/src/dataSource.ts +++ b/src/dataSource.ts @@ -7,7 +7,7 @@ import { AskpassEnvironment, AskpassManager } from './askpass/askpassManager'; import { getConfig } from './config'; import { Logger } from './logger'; import { CommitOrdering, DateType, DeepWriteable, ErrorInfo, GitCommit, GitCommitDetails, GitCommitStash, GitConfigLocation, GitFileChange, GitFileStatus, GitPushBranchMode, GitRepoConfig, GitRepoConfigBranches, GitResetMode, GitSignature, GitSignatureStatus, GitStash, GitTagDetails, MergeActionOn, RebaseActionOn, SquashMessageFormat, TagType, Writeable } from './types'; -import { GitExecutable, UNABLE_TO_FIND_GIT_MSG, UNCOMMITTED, abbrevCommit, constructIncompatibleGitVersionMessage, doesVersionMeetRequirement, getPathFromStr, getPathFromUri, openGitTerminal, pathWithTrailingSlash, realpath, resolveSpawnOutput, showErrorMessage } from './utils'; +import { GitExecutable, GitVersionRequirement, UNABLE_TO_FIND_GIT_MSG, UNCOMMITTED, abbrevCommit, constructIncompatibleGitVersionMessage, doesVersionMeetRequirement, getPathFromStr, getPathFromUri, openGitTerminal, pathWithTrailingSlash, realpath, resolveSpawnOutput, showErrorMessage } from './utils'; import { Disposable } from './utils/disposable'; import { Event } from './utils/event'; @@ -17,21 +17,15 @@ const INVALID_BRANCH_REGEXP = /^\(.* .*\)$/; const REMOTE_HEAD_BRANCH_REGEXP = /^remotes\/.*\/HEAD$/; const GIT_LOG_SEPARATOR = 'XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb'; -export const GIT_CONFIG = { - DIFF: { - GUI_TOOL: 'diff.guitool', - TOOL: 'diff.tool' - }, - REMOTE: { - PUSH_DEFAULT: 'remote.pushdefault' - }, - USER: { - EMAIL: 'user.email', - NAME: 'user.name' - } -}; +export const enum GitConfigKey { + DiffGuiTool = 'diff.guitool', + DiffTool = 'diff.tool', + RemotePushDefault = 'remote.pushdefault', + UserEmail = 'user.email', + UserName = 'user.name' +} -const GPG_STATUS_CODE_PARSING_DETAILS: { [statusCode: string]: GpgStatusCodeParsingDetails } = { +const GPG_STATUS_CODE_PARSING_DETAILS: Readonly<{ [statusCode: string]: GpgStatusCodeParsingDetails }> = { 'GOODSIG': { status: GitSignatureStatus.GoodAndValid, uid: true }, 'BADSIG': { status: GitSignatureStatus.Bad, uid: true }, 'ERRSIG': { status: GitSignatureStatus.CannotBeChecked, uid: false }, @@ -95,9 +89,9 @@ export class DataSource extends Disposable { * Set the Git executable used by the DataSource. * @param gitExecutable The Git executable. */ - public setGitExecutable(gitExecutable: GitExecutable | null) { + private setGitExecutable(gitExecutable: GitExecutable | null) { this.gitExecutable = gitExecutable; - this.gitExecutableSupportsGpgInfo = gitExecutable !== null ? doesVersionMeetRequirement(gitExecutable.version, '2.4.0') : false; + this.gitExecutableSupportsGpgInfo = gitExecutable !== null && doesVersionMeetRequirement(gitExecutable.version, GitVersionRequirement.GpgInfo); this.generateGitCommandFormats(); } @@ -314,9 +308,9 @@ export class DataSource extends Disposable { return { config: { branches: branches, - diffTool: getConfigValue(consolidatedConfigs, GIT_CONFIG.DIFF.TOOL), - guiDiffTool: getConfigValue(consolidatedConfigs, GIT_CONFIG.DIFF.GUI_TOOL), - pushDefault: getConfigValue(consolidatedConfigs, GIT_CONFIG.REMOTE.PUSH_DEFAULT), + diffTool: getConfigValue(consolidatedConfigs, GitConfigKey.DiffTool), + guiDiffTool: getConfigValue(consolidatedConfigs, GitConfigKey.DiffGuiTool), + pushDefault: getConfigValue(consolidatedConfigs, GitConfigKey.RemotePushDefault), remotes: remotes.map((remote) => ({ name: remote, url: getConfigValue(localConfigs, 'remote.' + remote + '.url'), @@ -324,12 +318,12 @@ export class DataSource extends Disposable { })), user: { name: { - local: getConfigValue(localConfigs, GIT_CONFIG.USER.NAME), - global: getConfigValue(globalConfigs, GIT_CONFIG.USER.NAME) + local: getConfigValue(localConfigs, GitConfigKey.UserName), + global: getConfigValue(globalConfigs, GitConfigKey.UserName) }, email: { - local: getConfigValue(localConfigs, GIT_CONFIG.USER.EMAIL), - global: getConfigValue(globalConfigs, GIT_CONFIG.USER.EMAIL) + local: getConfigValue(localConfigs, GitConfigKey.UserEmail), + global: getConfigValue(globalConfigs, GitConfigKey.UserEmail) } } }, @@ -503,8 +497,8 @@ export class DataSource extends Disposable { * @returns The tag details. */ public getTagDetails(repo: string, tagName: string): Promise { - if (this.gitExecutable !== null && !doesVersionMeetRequirement(this.gitExecutable.version, '1.7.8')) { - return Promise.resolve({ details: null, error: constructIncompatibleGitVersionMessage(this.gitExecutable, '1.7.8', 'retrieving Tag Details') }); + if (this.gitExecutable !== null && !doesVersionMeetRequirement(this.gitExecutable.version, GitVersionRequirement.TagDetails)) { + return Promise.resolve({ details: null, error: constructIncompatibleGitVersionMessage(this.gitExecutable, GitVersionRequirement.TagDetails, 'retrieving Tag Details') }); } const ref = 'refs/tags/' + tagName; @@ -758,8 +752,8 @@ export class DataSource extends Disposable { if (pruneTags) { if (!prune) { return Promise.resolve('In order to Prune Tags, pruning must also be enabled when fetching from ' + (remote !== null ? 'a remote' : 'remote(s)') + '.'); - } else if (this.gitExecutable !== null && !doesVersionMeetRequirement(this.gitExecutable.version, '2.17.0')) { - return Promise.resolve(constructIncompatibleGitVersionMessage(this.gitExecutable, '2.17.0', 'pruning tags when fetching')); + } else if (this.gitExecutable !== null && !doesVersionMeetRequirement(this.gitExecutable.version, GitVersionRequirement.FetchAndPruneTags)) { + return Promise.resolve(constructIncompatibleGitVersionMessage(this.gitExecutable, GitVersionRequirement.FetchAndPruneTags, 'pruning tags when fetching')); } args.push('--prune-tags'); } @@ -1139,23 +1133,23 @@ export class DataSource extends Disposable { /** * Set a configuration value for a repository. * @param repo The path of the repository. - * @param key The key to be set. + * @param key The Git Config Key to be set. * @param value The value to be set. * @param location The location where the configuration value should be set. * @returns The ErrorInfo from the executed command. */ - public setConfigValue(repo: string, key: string, value: string, location: GitConfigLocation) { + public setConfigValue(repo: string, key: GitConfigKey, value: string, location: GitConfigLocation) { return this.runGitCommand(['config', '--' + location, key, value], repo); } /** * Unset a configuration value for a repository. * @param repo The path of the repository. - * @param key The key to be unset. + * @param key The Git Config Key to be unset. * @param location The location where the configuration value should be unset. * @returns The ErrorInfo from the executed command. */ - public unsetConfigValue(repo: string, key: string, location: GitConfigLocation) { + public unsetConfigValue(repo: string, key: GitConfigKey, location: GitConfigLocation) { return this.runGitCommand(['config', '--' + location, '--unset-all', key], repo); } @@ -1236,8 +1230,8 @@ export class DataSource extends Disposable { public pushStash(repo: string, message: string, includeUntracked: boolean): Promise { if (this.gitExecutable === null) { return Promise.resolve(UNABLE_TO_FIND_GIT_MSG); - } else if (!doesVersionMeetRequirement(this.gitExecutable.version, '2.13.2')) { - return Promise.resolve(constructIncompatibleGitVersionMessage(this.gitExecutable, '2.13.2')); + } else if (!doesVersionMeetRequirement(this.gitExecutable.version, GitVersionRequirement.PushStash)) { + return Promise.resolve(constructIncompatibleGitVersionMessage(this.gitExecutable, GitVersionRequirement.PushStash)); } let args = ['stash', 'push']; @@ -1994,6 +1988,6 @@ interface GitTagDetailsData { } interface GpgStatusCodeParsingDetails { - status: GitSignatureStatus, - uid: boolean + readonly status: GitSignatureStatus, + readonly uid: boolean } diff --git a/src/gitGraphView.ts b/src/gitGraphView.ts index 3fa92f21..9dc0cc06 100644 --- a/src/gitGraphView.ts +++ b/src/gitGraphView.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import * as vscode from 'vscode'; import { AvatarManager } from './avatarManager'; import { getConfig } from './config'; -import { DataSource, GIT_CONFIG, GitCommitDetailsData } from './dataSource'; +import { DataSource, GitCommitDetailsData, GitConfigKey } from './dataSource'; import { ExtensionState } from './extensionState'; import { Logger } from './logger'; import { RepoFileWatcher } from './repoFileWatcher'; @@ -325,10 +325,10 @@ export class GitGraphView extends Disposable { case 'deleteUserDetails': errorInfos = []; if (msg.name) { - errorInfos.push(await this.dataSource.unsetConfigValue(msg.repo, GIT_CONFIG.USER.NAME, msg.location)); + errorInfos.push(await this.dataSource.unsetConfigValue(msg.repo, GitConfigKey.UserName, msg.location)); } if (msg.email) { - errorInfos.push(await this.dataSource.unsetConfigValue(msg.repo, GIT_CONFIG.USER.EMAIL, msg.location)); + errorInfos.push(await this.dataSource.unsetConfigValue(msg.repo, GitConfigKey.UserEmail, msg.location)); } this.sendMessage({ command: 'deleteUserDetails', @@ -355,15 +355,15 @@ export class GitGraphView extends Disposable { break; case 'editUserDetails': errorInfos = [ - await this.dataSource.setConfigValue(msg.repo, GIT_CONFIG.USER.NAME, msg.name, msg.location), - await this.dataSource.setConfigValue(msg.repo, GIT_CONFIG.USER.EMAIL, msg.email, msg.location) + await this.dataSource.setConfigValue(msg.repo, GitConfigKey.UserName, msg.name, msg.location), + await this.dataSource.setConfigValue(msg.repo, GitConfigKey.UserEmail, msg.email, msg.location) ]; if (errorInfos[0] === null && errorInfos[1] === null) { if (msg.deleteLocalName) { - errorInfos.push(await this.dataSource.unsetConfigValue(msg.repo, GIT_CONFIG.USER.NAME, GitConfigLocation.Local)); + errorInfos.push(await this.dataSource.unsetConfigValue(msg.repo, GitConfigKey.UserName, GitConfigLocation.Local)); } if (msg.deleteLocalEmail) { - errorInfos.push(await this.dataSource.unsetConfigValue(msg.repo, GIT_CONFIG.USER.EMAIL, GitConfigLocation.Local)); + errorInfos.push(await this.dataSource.unsetConfigValue(msg.repo, GitConfigKey.UserEmail, GitConfigLocation.Local)); } } this.sendMessage({ diff --git a/src/utils.ts b/src/utils.ts index a364617d..62c1e15f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -772,7 +772,18 @@ export async function getGitExecutableFromPaths(paths: string[]): Promise `version` is at least `requiredVersion`, FALSE => `version` is older than `requiredVersion`. */ -export function doesVersionMeetRequirement(version: string, requiredVersion: string) { +export function doesVersionMeetRequirement(version: string, requiredVersion: GitVersionRequirement | VsCodeVersionRequirement) { const v1 = parseVersion(version); const v2 = parseVersion(requiredVersion); @@ -824,10 +835,10 @@ function parseVersion(version: string) { /** * Construct a message that explains to the user that the Git executable is not compatible with a feature. * @param executable The Git executable. - * @param version The minimum required version number. + * @param version The minimum required version. * @param feature An optional name for the feature. * @returns The message for the user. */ -export function constructIncompatibleGitVersionMessage(executable: GitExecutable, version: string, feature?: string) { +export function constructIncompatibleGitVersionMessage(executable: GitExecutable, version: GitVersionRequirement, feature?: string) { return 'A newer version of Git (>= ' + version + ') is required for ' + (feature ? feature : 'this feature') + '. Git ' + executable.version + ' is currently installed. Please install a newer version of Git to use this feature.'; } diff --git a/tests/dataSource.test.ts b/tests/dataSource.test.ts index a7316ba3..07143639 100644 --- a/tests/dataSource.test.ts +++ b/tests/dataSource.test.ts @@ -12,7 +12,7 @@ import * as fs from 'fs'; import * as iconv from 'iconv-lite'; import * as path from 'path'; import { ConfigurationChangeEvent } from 'vscode'; -import { DataSource } from '../src/dataSource'; +import { DataSource, GitConfigKey } from '../src/dataSource'; import { Logger } from '../src/logger'; import { CommitOrdering, GitConfigLocation, GitPushBranchMode, GitResetMode, GitSignature, GitSignatureStatus, MergeActionOn, RebaseActionOn, TagType } from '../src/types'; import * as utils from '../src/utils'; @@ -114,6 +114,33 @@ describe('DataSource', () => { }); }); + describe('setGitExecutable', () => { + it('Should set gitExecutableSupportsGpgInfo to FALSE when there is no Git executable', () => { + // Run + dataSource.dispose(); + dataSource = new DataSource(null, onDidChangeConfiguration.subscribe, onDidChangeGitExecutable.subscribe, logger); + + // Assert + expect(dataSource['gitExecutableSupportsGpgInfo']).toBe(false); + }); + + it('Should set gitExecutableSupportsGpgInfo to FALSE when the Git executable is older than 2.4.0', () => { + // Run + onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '2.3.0' }); + + // Assert + expect(dataSource['gitExecutableSupportsGpgInfo']).toBe(false); + }); + + it('Should set gitExecutableSupportsGpgInfo to TRUE when the Git executable is at least 2.4.0', () => { + // Run + onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '2.4.0' }); + + // Assert + expect(dataSource['gitExecutableSupportsGpgInfo']).toBe(true); + }); + }); + describe('getRepoInfo', () => { it('Should return the repository info', async () => { // Setup @@ -2742,6 +2769,7 @@ describe('DataSource', () => { vscode.mockExtensionSettingReturnValue('date.type', 'Author Date'); vscode.mockExtensionSettingReturnValue('repository.useMailmap', false); vscode.mockExtensionSettingReturnValue('repository.commits.showSignatureStatus', true); + onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '2.4.0' }); // Run onDidChangeConfiguration.emit({ @@ -2805,6 +2833,7 @@ describe('DataSource', () => { vscode.mockExtensionSettingReturnValue('date.type', 'Author Date'); vscode.mockExtensionSettingReturnValue('repository.useMailmap', false); vscode.mockExtensionSettingReturnValue('showSignatureStatus', true); + onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '2.4.0' }); // Run onDidChangeConfiguration.emit({ @@ -2860,7 +2889,7 @@ describe('DataSource', () => { expect(spyOnSpawn).toBeCalledWith('/path/to/git', ['diff', '--numstat', '--find-renames', '--diff-filter=AMDR', '-z', '1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b^', '1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b'], expect.objectContaining({ cwd: '/path/to/repo' })); }); - it('Should return the commit details (without signature status if not available)', async () => { + it('Should return the commit details (without signature status) when Git is older than 2.4.0', async () => { // Setup mockGitSuccessOnce('1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2bXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPba1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbTest AuthorXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbtest-author@mhutchie.comXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb1587559258XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbTest CommitterXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbtest-committer@mhutchie.comXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb1587559259XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbCommit Message.\r\nSecond Line.'); mockGitSuccessOnce(['D', 'dir/deleted.txt', 'M', 'dir/modified.txt', 'R100', 'dir/renamed-old.txt', 'dir/renamed-new.txt', ''].join('\0')); @@ -2868,12 +2897,9 @@ describe('DataSource', () => { vscode.mockExtensionSettingReturnValue('date.type', 'Author Date'); vscode.mockExtensionSettingReturnValue('repository.useMailmap', false); vscode.mockExtensionSettingReturnValue('repository.commits.showSignatureStatus', true); + onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '2.3.0' }); // Run - onDidChangeGitExecutable.emit({ - path: '/path/to/git', - version: '2.3.0' - }); const result = await dataSource.getCommitDetails('/path/to/repo', '1a2b3c4d5e6f1a2b3c4d5e6f1a2b3c4d5e6f1a2b', true); // Assert @@ -3855,6 +3881,7 @@ describe('DataSource', () => { describe('getTagDetails', () => { it('Should return the tag\'s details', async () => { // Setup + onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '1.7.8' }); mockGitSuccessOnce('79e88e142b378f41dfd1f82d94209a7a411384edXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbTest TaggerXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb1587559258XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbsubject1\r\nsubject2\n\nbody1\nbody2\n\n'); // Run @@ -3878,7 +3905,6 @@ describe('DataSource', () => { it('Should return the tag\'s details (when email isn\'t enclosed by <>)', async () => { // Setup - onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '1.7.8' }); mockGitSuccessOnce('79e88e142b378f41dfd1f82d94209a7a411384edXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbTest TaggerXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbtest@mhutchie.comXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb1587559258XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbXX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPbtag-message\n'); // Run @@ -3923,7 +3949,7 @@ describe('DataSource', () => { expect(spyOnSpawn).toBeCalledWith('/path/to/git', ['for-each-ref', 'refs/tags/tag-name', '--format=%(objectname)XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb%(taggername)XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb%(taggeremail)XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb%(taggerdate:unix)XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb%(contents:signature)XX7Nal-YARtTpjCikii9nJxER19D6diSyk-AWkPb%(contents)'], expect.objectContaining({ cwd: '/path/to/repo' })); }); - it('Should return an error message when viewing tag details with Git < 1.7.8', async () => { + it('Should return the "Incompatible Git Version" error message when viewing tag details and Git is older than 1.7.8', async () => { // Setup onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '1.7.7' }); @@ -3938,6 +3964,22 @@ describe('DataSource', () => { expect(spyOnSpawn).toHaveBeenCalledTimes(0); }); + it('Should return the "Unable to Find Git" error message when no Git executable is known', async () => { + // Setup + dataSource.dispose(); + dataSource = new DataSource(null, onDidChangeConfiguration.subscribe, onDidChangeGitExecutable.subscribe, logger); + + // Run + const result = await dataSource.getTagDetails('/path/to/repo', 'tag-name'); + + // Assert + expect(result).toStrictEqual({ + details: null, + error: 'Unable to find a Git executable. Either: Set the Visual Studio Code Setting "git.path" to the path and filename of an existing Git executable, or install Git and restart Visual Studio Code.' + }); + expect(spyOnSpawn).toHaveBeenCalledTimes(0); + }); + it('Should return an error message thrown by git', async () => { // Setup mockGitThrowingErrorOnce(); @@ -4819,7 +4861,7 @@ describe('DataSource', () => { expect(result).toBe('Unable to find a Git executable. Either: Set the Visual Studio Code Setting "git.path" to the path and filename of an existing Git executable, or install Git and restart Visual Studio Code.'); }); - it('Should return an error message when pruning tags with Git < 2.17.0', async () => { + it('Should return the "Incompatible Git Version" error message when pruning tags and Git is older than 2.17.0', async () => { // Setup onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '2.16.1' }); @@ -6113,7 +6155,7 @@ describe('DataSource', () => { mockGitSuccessOnce(); // Run - const result = await dataSource.setConfigValue('/path/to/repo', 'user.name', 'Test User Name', GitConfigLocation.Global); + const result = await dataSource.setConfigValue('/path/to/repo', GitConfigKey.UserName, 'Test User Name', GitConfigLocation.Global); // Assert expect(result).toBe(null); @@ -6125,7 +6167,7 @@ describe('DataSource', () => { mockGitSuccessOnce(); // Run - const result = await dataSource.setConfigValue('/path/to/repo', 'user.name', 'Test User Name', GitConfigLocation.Local); + const result = await dataSource.setConfigValue('/path/to/repo', GitConfigKey.UserName, 'Test User Name', GitConfigLocation.Local); // Assert expect(result).toBe(null); @@ -6137,7 +6179,7 @@ describe('DataSource', () => { mockGitSuccessOnce(); // Run - const result = await dataSource.setConfigValue('/path/to/repo', 'user.name', 'Test User Name', GitConfigLocation.System); + const result = await dataSource.setConfigValue('/path/to/repo', GitConfigKey.UserName, 'Test User Name', GitConfigLocation.System); // Assert expect(result).toBe(null); @@ -6149,7 +6191,7 @@ describe('DataSource', () => { mockGitThrowingErrorOnce(); // Run - const result = await dataSource.setConfigValue('/path/to/repo', 'user.name', 'Test User Name', GitConfigLocation.Global); + const result = await dataSource.setConfigValue('/path/to/repo', GitConfigKey.UserName, 'Test User Name', GitConfigLocation.Global); // Assert expect(result).toBe('error message'); @@ -6162,7 +6204,7 @@ describe('DataSource', () => { mockGitSuccessOnce(); // Run - const result = await dataSource.unsetConfigValue('/path/to/repo', 'user.name', GitConfigLocation.Global); + const result = await dataSource.unsetConfigValue('/path/to/repo', GitConfigKey.UserName, GitConfigLocation.Global); // Assert expect(result).toBe(null); @@ -6174,7 +6216,7 @@ describe('DataSource', () => { mockGitSuccessOnce(); // Run - const result = await dataSource.unsetConfigValue('/path/to/repo', 'user.name', GitConfigLocation.Local); + const result = await dataSource.unsetConfigValue('/path/to/repo', GitConfigKey.UserName, GitConfigLocation.Local); // Assert expect(result).toBe(null); @@ -6186,7 +6228,7 @@ describe('DataSource', () => { mockGitSuccessOnce(); // Run - const result = await dataSource.unsetConfigValue('/path/to/repo', 'user.name', GitConfigLocation.System); + const result = await dataSource.unsetConfigValue('/path/to/repo', GitConfigKey.UserName, GitConfigLocation.System); // Assert expect(result).toBe(null); @@ -6198,7 +6240,7 @@ describe('DataSource', () => { mockGitThrowingErrorOnce(); // Run - const result = await dataSource.unsetConfigValue('/path/to/repo', 'user.name', GitConfigLocation.Global); + const result = await dataSource.unsetConfigValue('/path/to/repo', GitConfigKey.UserName, GitConfigLocation.Global); // Assert expect(result).toBe('error message'); @@ -6369,6 +6411,7 @@ describe('DataSource', () => { describe('pushStash', () => { it('Should push the uncommitted changes to a stash', async () => { // Setup + onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '2.13.2' }); mockGitSuccessOnce(); // Run @@ -6426,10 +6469,9 @@ describe('DataSource', () => { expect(result).toBe('Unable to find a Git executable. Either: Set the Visual Studio Code Setting "git.path" to the path and filename of an existing Git executable, or install Git and restart Visual Studio Code.'); }); - it('Should return the "Incompatible Git Version" error message if git is older than 2.13.2', async () => { + it('Should return the "Incompatible Git Version" error message when Git is older than 2.13.2', async () => { // Setup - dataSource.dispose(); - dataSource = new DataSource({ path: '/path/to/git', version: '2.13.1' }, onDidChangeConfiguration.subscribe, onDidChangeGitExecutable.subscribe, logger); + onDidChangeGitExecutable.emit({ path: '/path/to/git', version: '2.13.1' }); // Run const result = await dataSource.pushStash('/path/to/repo', '', false); diff --git a/tests/utils.test.ts b/tests/utils.test.ts index ab2d31c3..9770a794 100644 --- a/tests/utils.test.ts +++ b/tests/utils.test.ts @@ -24,7 +24,7 @@ import { DataSource } from '../src/dataSource'; import { DEFAULT_REPO_STATE, ExtensionState } from '../src/extensionState'; import { Logger } from '../src/logger'; import { GitFileStatus, PullRequestProvider, RepoDropdownOrder } from '../src/types'; -import { GitExecutable, UNCOMMITTED, abbrevCommit, abbrevText, archive, constructIncompatibleGitVersionMessage, copyFilePathToClipboard, copyToClipboard, createPullRequest, doesFileExist, doesVersionMeetRequirement, evalPromises, findGit, getExtensionVersion, getGitExecutable, getGitExecutableFromPaths, getNonce, getPathFromStr, getPathFromUri, getRelativeTimeDiff, getRepoName, getSortedRepositoryPaths, isPathInWorkspace, openExtensionSettings, openExternalUrl, openFile, openGitTerminal, pathWithTrailingSlash, realpath, resolveSpawnOutput, resolveToSymbolicPath, showErrorMessage, showInformationMessage, viewDiff, viewDiffWithWorkingFile, viewFileAtRevision, viewScm } from '../src/utils'; +import { GitExecutable, GitVersionRequirement, UNCOMMITTED, abbrevCommit, abbrevText, archive, constructIncompatibleGitVersionMessage, copyFilePathToClipboard, copyToClipboard, createPullRequest, doesFileExist, doesVersionMeetRequirement, evalPromises, findGit, getExtensionVersion, getGitExecutable, getGitExecutableFromPaths, getNonce, getPathFromStr, getPathFromUri, getRelativeTimeDiff, getRepoName, getSortedRepositoryPaths, isPathInWorkspace, openExtensionSettings, openExternalUrl, openFile, openGitTerminal, pathWithTrailingSlash, realpath, resolveSpawnOutput, resolveToSymbolicPath, showErrorMessage, showInformationMessage, viewDiff, viewDiffWithWorkingFile, viewFileAtRevision, viewScm } from '../src/utils'; import { EventEmitter } from '../src/utils/event'; const extensionContext = vscode.mocks.extensionContext; @@ -2393,7 +2393,7 @@ describe('getGitExecutableFromPaths', () => { describe('doesVersionMeetRequirement', () => { it('Should correctly determine major newer', () => { // Run - const result = doesVersionMeetRequirement('2.4.6.windows.0', '1.4.6'); + const result = doesVersionMeetRequirement('2.7.8.windows.0', GitVersionRequirement.TagDetails); // Assert expect(result).toBe(true); @@ -2401,7 +2401,7 @@ describe('doesVersionMeetRequirement', () => { it('Should correctly determine major older', () => { // Run - const result = doesVersionMeetRequirement('2.4.6.windows.0', '3.4.6'); + const result = doesVersionMeetRequirement('0.7.8.windows.0', GitVersionRequirement.TagDetails); // Assert expect(result).toBe(false); @@ -2409,7 +2409,7 @@ describe('doesVersionMeetRequirement', () => { it('Should correctly determine minor newer', () => { // Run - const result = doesVersionMeetRequirement('2.4.6 (Apple Git-122.3)', '2.3.6'); + const result = doesVersionMeetRequirement('1.8.8 (Apple Git-122.3)', GitVersionRequirement.TagDetails); // Assert expect(result).toBe(true); @@ -2417,7 +2417,7 @@ describe('doesVersionMeetRequirement', () => { it('Should correctly determine minor older', () => { // Run - const result = doesVersionMeetRequirement('2.4.6 (Apple Git-122.3)', '2.5.6'); + const result = doesVersionMeetRequirement('1.6.8 (Apple Git-122.3)', GitVersionRequirement.TagDetails); // Assert expect(result).toBe(false); @@ -2425,7 +2425,7 @@ describe('doesVersionMeetRequirement', () => { it('Should correctly determine patch newer', () => { // Run - const result = doesVersionMeetRequirement('2.4.6', '2.4.5'); + const result = doesVersionMeetRequirement('1.7.9', GitVersionRequirement.TagDetails); // Assert expect(result).toBe(true); @@ -2433,7 +2433,7 @@ describe('doesVersionMeetRequirement', () => { it('Should correctly determine patch older', () => { // Run - const result = doesVersionMeetRequirement('2.4.6', '2.4.7'); + const result = doesVersionMeetRequirement('1.7.7', GitVersionRequirement.TagDetails); // Assert expect(result).toBe(false); @@ -2441,7 +2441,7 @@ describe('doesVersionMeetRequirement', () => { it('Should correctly determine same version', () => { // Run - const result = doesVersionMeetRequirement('2.4.6', '2.4.6'); + const result = doesVersionMeetRequirement('1.7.8', GitVersionRequirement.TagDetails); // Assert expect(result).toBe(true); @@ -2449,7 +2449,7 @@ describe('doesVersionMeetRequirement', () => { it('Should correctly determine major newer if missing patch version', () => { // Run - const result = doesVersionMeetRequirement('2.4', '1.4'); + const result = doesVersionMeetRequirement('2.7', GitVersionRequirement.TagDetails); // Assert expect(result).toBe(true); @@ -2457,7 +2457,7 @@ describe('doesVersionMeetRequirement', () => { it('Should correctly determine major newer if missing minor & patch versions', () => { // Run - const result = doesVersionMeetRequirement('2', '1'); + const result = doesVersionMeetRequirement('2', GitVersionRequirement.TagDetails); // Assert expect(result).toBe(true); @@ -2465,13 +2465,13 @@ describe('doesVersionMeetRequirement', () => { it('Should only use the valid portion of the version number to compute the result', () => { // Run - const result1 = doesVersionMeetRequirement('2.4..6-windows.0', '2.4.1'); + const result1 = doesVersionMeetRequirement('1.7..8-windows.0', GitVersionRequirement.TagDetails); // Assert expect(result1).toBe(false); // Run - const result2 = doesVersionMeetRequirement('2.4..6-windows.0', '2.4.0'); + const result2 = doesVersionMeetRequirement('1.8..7-windows.0', GitVersionRequirement.TagDetails); // Assert expect(result2).toBe(true); @@ -2479,15 +2479,7 @@ describe('doesVersionMeetRequirement', () => { it('Should return TRUE if executable version is invalid', () => { // Run - const result = doesVersionMeetRequirement('a2.4.6', '1.4.6'); - - // Assert - expect(result).toBe(true); - }); - - it('Should return TRUE if version is invalid', () => { - // Run - const result = doesVersionMeetRequirement('2.4.6', 'a1.4.6'); + const result = doesVersionMeetRequirement('a1.7.7', GitVersionRequirement.TagDetails); // Assert expect(result).toBe(true); @@ -2497,10 +2489,10 @@ describe('doesVersionMeetRequirement', () => { describe('constructIncompatibleGitVersionMessage', () => { it('Should return the constructed message', () => { // Run - const result = constructIncompatibleGitVersionMessage({ version: '2.4.5', path: '' }, '3.0.0'); + const result = constructIncompatibleGitVersionMessage({ version: '1.7.7', path: '' }, GitVersionRequirement.TagDetails); // Assert - expect(result).toBe('A newer version of Git (>= 3.0.0) is required for this feature. Git 2.4.5 is currently installed. Please install a newer version of Git to use this feature.'); + expect(result).toBe('A newer version of Git (>= 1.7.8) is required for this feature. Git 1.7.7 is currently installed. Please install a newer version of Git to use this feature.'); }); });