diff --git a/src/tfvc/commands/resolveconflicts.ts b/src/tfvc/commands/resolveconflicts.ts index 65e3073a2..767ed629d 100644 --- a/src/tfvc/commands/resolveconflicts.ts +++ b/src/tfvc/commands/resolveconflicts.ts @@ -79,6 +79,6 @@ export class ResolveConflicts implements ITfvcCommand { } public async ParseExeOutput(executionResult: IExecutionResult): Promise { - return this.ParseOutput(executionResult); + return await this.ParseOutput(executionResult); } } diff --git a/src/tfvc/commands/sync.ts b/src/tfvc/commands/sync.ts index ffdf90af6..ff68d22ad 100644 --- a/src/tfvc/commands/sync.ts +++ b/src/tfvc/commands/sync.ts @@ -108,7 +108,7 @@ export class Sync implements ITfvcCommand { } public async ParseExeOutput(executionResult: IExecutionResult): Promise { - return this.ParseOutput(executionResult); + return await this.ParseOutput(executionResult); } private getItemResults(stdout: string): ISyncItemResult[] { @@ -177,7 +177,7 @@ export class Sync implements ITfvcCommand { index = line.indexOf("cannot be deleted"); if (index >= 0) { newResult = { - syncType: SyncType.Error, + syncType: SyncType.Warning, itemPath: CommandHelper.GetFilePath(folderPath, line.slice(0, index).trim()), message: line.trim() }; diff --git a/src/tfvc/commands/undo.ts b/src/tfvc/commands/undo.ts index 04dccb5fe..084af1ea2 100644 --- a/src/tfvc/commands/undo.ts +++ b/src/tfvc/commands/undo.ts @@ -84,7 +84,7 @@ export class Undo implements ITfvcCommand { } public async ParseExeOutput(executionResult: IExecutionResult): Promise { - return this.ParseOutput(executionResult); + return await this.ParseOutput(executionResult); } //line could be 'Undoing edit: file1.txt', 'Undoing add: file1.txt' diff --git a/test/tfvc/commands/resolveconflicts.test.ts b/test/tfvc/commands/resolveconflicts.test.ts index e9373229b..a13661d18 100644 --- a/test/tfvc/commands/resolveconflicts.test.ts +++ b/test/tfvc/commands/resolveconflicts.test.ts @@ -62,6 +62,12 @@ describe("Tfvc-ResolveConflictsCommand", function() { assert.deepEqual(cmd.GetOptions(), {}); }); + it("should verify GetExeOptions", function() { + let localPaths: string[] = ["/usr/alias/repo1/file.txt"]; + let cmd: ResolveConflicts = new ResolveConflicts(undefined, localPaths, AutoResolveType.KeepYours); + assert.deepEqual(cmd.GetExeOptions(), {}); + }); + it("should verify arguments", function() { let localPaths: string[] = ["/usr/alias/repo1/file.txt"]; let cmd: ResolveConflicts = new ResolveConflicts(undefined, localPaths, AutoResolveType.KeepYours); @@ -83,6 +89,27 @@ describe("Tfvc-ResolveConflictsCommand", function() { assert.equal(cmd.GetArguments().GetArgumentsForDisplay(), "resolve -noprompt -collection:" + collectionUrl + " ******** " + localPaths[0] + " -auto:TakeTheirs"); }); + it("should verify GetExeArguments", function() { + let localPaths: string[] = ["/usr/alias/repo1/file.txt"]; + let cmd: ResolveConflicts = new ResolveConflicts(undefined, localPaths, AutoResolveType.KeepYours); + + assert.equal(cmd.GetExeArguments().GetArgumentsForDisplay(), "resolve -noprompt " + localPaths[0] + " -auto:KeepYours"); + }); + + it("should verify GetExeArguments with context", function() { + let localPaths: string[] = ["/usr/alias/repo1/file.txt"]; + let cmd: ResolveConflicts = new ResolveConflicts(context, localPaths, AutoResolveType.KeepYours); + + assert.equal(cmd.GetExeArguments().GetArgumentsForDisplay(), "resolve -noprompt ******** " + localPaths[0] + " -auto:KeepYours"); + }); + + it("should verify GetExeArguments with TakeTheirs", function() { + let localPaths: string[] = ["/usr/alias/repo1/file.txt"]; + let cmd: ResolveConflicts = new ResolveConflicts(context, localPaths, AutoResolveType.TakeTheirs); + + assert.equal(cmd.GetExeArguments().GetArgumentsForDisplay(), "resolve -noprompt ******** " + localPaths[0] + " -auto:TakeTheirs"); + }); + it("should verify parse output - no output", async function() { let localPaths: string[] = ["/usr/alias/repo1/file.txt"]; let cmd: ResolveConflicts = new ResolveConflicts(undefined, localPaths, AutoResolveType.KeepYours); @@ -132,4 +159,58 @@ describe("Tfvc-ResolveConflictsCommand", function() { assert.equal(err.stdout.indexOf("Something bad this way comes."), 0); } }); + + /*********************************************************************************************** + * The methods below are duplicates of the parse output methods but call the parseExeOutput. + ***********************************************************************************************/ + + it("should verify parse EXE output - no output", async function() { + let localPaths: string[] = ["/usr/alias/repo1/file.txt"]; + let cmd: ResolveConflicts = new ResolveConflicts(undefined, localPaths, AutoResolveType.KeepYours); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: undefined, + stderr: undefined + }; + + let results: IConflict[] = await cmd.ParseExeOutput(executionResult); + assert.equal(results.length, 0); + }); + + it("should verify parse EXE output - no errors", async function() { + let localPaths: string[] = ["/usr/alias/repo1/file.txt", "/usr/alias/repo1/file2.txt"]; + let cmd: ResolveConflicts = new ResolveConflicts(undefined, localPaths, AutoResolveType.KeepYours); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: "Resolved /usr/alias/repo1/file.txt as KeepYours\n" + + "Resolved /usr/alias/repo1/file2.txt as KeepYours", + stderr: undefined + }; + + let results: IConflict[] = await cmd.ParseExeOutput(executionResult); + assert.equal(results.length, 2); + assert.equal(results[0].localPath, "/usr/alias/repo1/file.txt"); + assert.equal(results[0].type, ConflictType.RESOLVED); + assert.equal(results[1].localPath, "/usr/alias/repo1/file2.txt"); + assert.equal(results[1].type, ConflictType.RESOLVED); + }); + + it("should verify parse EXE output - errors - exit code 100", async function() { + let localPaths: string[] = ["/usr/alias/repo1/file.txt"]; + let cmd: ResolveConflicts = new ResolveConflicts(undefined, localPaths, AutoResolveType.KeepYours); + let executionResult: IExecutionResult = { + exitCode: 100, + stdout: "Something bad this way comes.", + stderr: undefined + }; + + try { + await cmd.ParseExeOutput(executionResult); + } catch (err) { + assert.equal(err.exitCode, 100); + assert.equal(err.tfvcCommand, "resolve"); + assert.equal(err.message.indexOf(Strings.TfExecFailedError), 0); + assert.equal(err.stdout.indexOf("Something bad this way comes."), 0); + } + }); }); diff --git a/test/tfvc/commands/sync.test.ts b/test/tfvc/commands/sync.test.ts index 488c07a39..15e6bfb4f 100644 --- a/test/tfvc/commands/sync.test.ts +++ b/test/tfvc/commands/sync.test.ts @@ -62,6 +62,12 @@ describe("Tfvc-SyncCommand", function() { assert.deepEqual(cmd.GetOptions(), {}); }); + it("should verify GetExeOptions", function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(undefined, localPaths, false); + assert.deepEqual(cmd.GetExeOptions(), {}); + }); + it("should verify arguments", function() { let localPaths: string[] = ["/usr/alias/repo1"]; let cmd: Sync = new Sync(undefined, localPaths, false); @@ -83,6 +89,27 @@ describe("Tfvc-SyncCommand", function() { assert.equal(cmd.GetArguments().GetArgumentsForDisplay(), "get -noprompt -collection:" + collectionUrl + " ******** -nosummary " + localPaths[0] + " -recursive"); }); + it("should verify getExeArguments", function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(undefined, localPaths, false); + + assert.equal(cmd.GetExeArguments().GetArgumentsForDisplay(), "get -noprompt -nosummary " + localPaths[0]); + }); + + it("should verify getExeArguments with context", function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(context, localPaths, false); + + assert.equal(cmd.GetExeArguments().GetArgumentsForDisplay(), "get -noprompt ******** -nosummary " + localPaths[0]); + }); + + it("should verify getExeArguments with context and recursive", function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(context, localPaths, true); + + assert.equal(cmd.GetExeArguments().GetArgumentsForDisplay(), "get -noprompt ******** -nosummary " + localPaths[0] + " -recursive"); + }); + it("should verify parse output - no output", async function() { let localPaths: string[] = ["/usr/alias/repo1"]; let cmd: Sync = new Sync(undefined, localPaths, true); @@ -250,11 +277,12 @@ describe("Tfvc-SyncCommand", function() { exitCode: 1, stdout: "/usr/alias/repo1:\n", stderr: "new4.txt - Unable to perform the get operation because you have a conflicting rename (to be moved from /path/new5.txt)\n" + - "Warning new111.txt - Unable to perform the get operation because it is writable" + "Warning new111.txt - Unable to perform the get operation because it is writable\n" + + "/usr/alias/repo1/folder cannot be deleted because it is not empty" }; let results: ISyncResults = await cmd.ParseOutput(executionResult); - assert.equal(results.itemResults.length, 2); + assert.equal(results.itemResults.length, 3); assert.equal(results.hasConflicts, false); assert.equal(results.hasErrors, true); assert.equal(results.itemResults[0].syncType, SyncType.Error); @@ -263,6 +291,9 @@ describe("Tfvc-SyncCommand", function() { assert.equal(results.itemResults[1].syncType, SyncType.Warning); assert.equal(results.itemResults[1].itemPath, "new111.txt"); assert.equal(results.itemResults[1].message, "Unable to perform the get operation because it is writable"); + assert.equal(results.itemResults[2].syncType, SyncType.Warning); + assert.equal(results.itemResults[2].itemPath, "/usr/alias/repo1/folder"); + assert.equal(results.itemResults[2].message, "/usr/alias/repo1/folder cannot be deleted because it is not empty"); }); it("should verify parse output - errors - exit code 100", async function() { @@ -283,4 +314,214 @@ describe("Tfvc-SyncCommand", function() { assert.equal(err.stdout.indexOf("Something bad this way comes."), 0); } }); + + /*********************************************************************************************** + * The methods below are duplicates of the parse output methods but call the parseExeOutput. + ***********************************************************************************************/ + + it("should verify parse EXE output - no output", async function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(undefined, localPaths, true); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: undefined, + stderr: undefined + }; + + let results: ISyncResults = await cmd.ParseExeOutput(executionResult); + assert.equal(results.itemResults.length, 0); + assert.equal(results.hasConflicts, false); + assert.equal(results.hasErrors, false); + }); + + it("should verify parse EXE output - up to date", async function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(undefined, localPaths, true); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: "All files are up to date.", + stderr: undefined + }; + + let results: ISyncResults = await cmd.ParseExeOutput(executionResult); + assert.equal(results.itemResults.length, 0); + assert.equal(results.hasConflicts, false); + assert.equal(results.hasErrors, false); + }); + + it("should verify parse EXE output - single file edit - no errors", async function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(undefined, localPaths, true); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: "/usr/alias/repo1/test:\n" + + "Replacing test1.txt\n", + stderr: undefined + }; + + let results: ISyncResults = await cmd.ParseExeOutput(executionResult); + assert.equal(results.itemResults.length, 1); + assert.equal(results.hasConflicts, false); + assert.equal(results.hasErrors, false); + assert.equal(results.itemResults[0].syncType, SyncType.Updated); + assert.equal(results.itemResults[0].itemPath, path.join("/usr/alias/repo1/test", "test1.txt")); + }); + + it("should verify parse EXE output - single file add - no errors", async function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(undefined, localPaths, true); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: "/usr/alias/repo1/test:\n" + + "Getting test1.txt\n", + stderr: undefined + }; + + let results: ISyncResults = await cmd.ParseExeOutput(executionResult); + assert.equal(results.itemResults.length, 1); + assert.equal(results.hasConflicts, false); + assert.equal(results.hasErrors, false); + assert.equal(results.itemResults[0].syncType, SyncType.New); + assert.equal(results.itemResults[0].itemPath, path.join("/usr/alias/repo1/test", "test1.txt")); + }); + + it("should verify parse EXE output - single file add - spaces - no errors", async function() { + let localPaths: string[] = ["/usr/alias/repo 1"]; + let cmd: Sync = new Sync(undefined, localPaths, true); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: "/usr/alias/repo 1/test:\n" + + "Getting test 1.txt\n", + stderr: undefined + }; + + let results: ISyncResults = await cmd.ParseExeOutput(executionResult); + assert.equal(results.itemResults.length, 1); + assert.equal(results.hasConflicts, false); + assert.equal(results.hasErrors, false); + assert.equal(results.itemResults[0].syncType, SyncType.New); + assert.equal(results.itemResults[0].itemPath, path.join("/usr/alias/repo 1/test", "test 1.txt")); + }); + + it("should verify parse EXE output - multiple files - with conflict", async function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(undefined, localPaths, true); + let executionResult: IExecutionResult = { + exitCode: 1, + stdout: "/usr/alias/repo1:\n" + + "Getting addFold\n" + + "Getting addFold-branch\n" + + "\n" + + "/usr/alias/repo1/addFold-branch:\n" + + "Replacing testHereRename.txt\n" + + "\n" + + "/usr/alias/repo1/addFold:\n" + + "Getting testHere3\n" + + "Replacing testHereRename7.txt\n" + + "\n" + + "/usr/alias/repo1/last:\n" + + "Deleting Rename2.txt\n" + + "Replacing test3.txt\n" + + "Getting TestAdd.txt\n" + + "\n", + stderr: "Conflict test_renamed.txt - Unable to perform the get operation because you have a conflicting rename, edit\n" + }; + + let results: ISyncResults = await cmd.ParseExeOutput(executionResult); + assert.equal(results.itemResults.length, 9); + assert.equal(results.hasConflicts, true); + assert.equal(results.hasErrors, false); + assert.equal(results.itemResults[0].syncType, SyncType.New); + assert.equal(results.itemResults[0].itemPath, path.join("/usr/alias/repo1", "addFold")); + assert.equal(results.itemResults[1].syncType, SyncType.New); + assert.equal(results.itemResults[1].itemPath, path.join("/usr/alias/repo1", "addFold-branch")); + assert.equal(results.itemResults[2].syncType, SyncType.Updated); + assert.equal(results.itemResults[2].itemPath, path.join("/usr/alias/repo1/addFold-branch", "testHereRename.txt")); + assert.equal(results.itemResults[3].syncType, SyncType.New); + assert.equal(results.itemResults[3].itemPath, path.join("/usr/alias/repo1/addFold", "testHere3")); + assert.equal(results.itemResults[4].syncType, SyncType.Updated); + assert.equal(results.itemResults[4].itemPath, path.join("/usr/alias/repo1/addFold", "testHereRename7.txt")); + assert.equal(results.itemResults[5].syncType, SyncType.Deleted); + assert.equal(results.itemResults[5].itemPath, path.join("/usr/alias/repo1/last", "Rename2.txt")); + assert.equal(results.itemResults[6].syncType, SyncType.Updated); + assert.equal(results.itemResults[6].itemPath, path.join("/usr/alias/repo1/last", "test3.txt")); + assert.equal(results.itemResults[7].syncType, SyncType.New); + assert.equal(results.itemResults[7].itemPath, path.join("/usr/alias/repo1/last", "TestAdd.txt")); + //stderr lines always come last + assert.equal(results.itemResults[8].syncType, SyncType.Conflict); + assert.equal(results.itemResults[8].itemPath, "test_renamed.txt"); + assert.equal(results.itemResults[8].message, "Unable to perform the get operation because you have a conflicting rename, edit"); + }); + + it("should verify parse EXE output - errors - exit code 1", async function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(undefined, localPaths, true); + let executionResult: IExecutionResult = { + exitCode: 1, + stdout: "/usr/alias/repo1:\n", + stderr: "Conflict new.txt - Unable to perform the get operation because you have a conflicting edit\n" + + "new4.txt - Unable to perform the get operation because you have a conflicting rename (to be moved from /path/new5.txt)\n" + + "Warning new111.txt - Unable to perform the get operation because it is writable" + }; + + let results: ISyncResults = await cmd.ParseExeOutput(executionResult); + assert.equal(results.itemResults.length, 3); + assert.equal(results.hasConflicts, true); + assert.equal(results.hasErrors, true); + assert.equal(results.itemResults[0].syncType, SyncType.Conflict); + assert.equal(results.itemResults[0].itemPath, "new.txt"); + assert.equal(results.itemResults[0].message, "Unable to perform the get operation because you have a conflicting edit"); + assert.equal(results.itemResults[1].syncType, SyncType.Error); + assert.equal(results.itemResults[1].itemPath, "new4.txt"); + assert.equal(results.itemResults[1].message, "Unable to perform the get operation because you have a conflicting rename (to be moved from /path/new5.txt)"); + assert.equal(results.itemResults[2].syncType, SyncType.Warning); + assert.equal(results.itemResults[2].itemPath, "new111.txt"); + assert.equal(results.itemResults[2].message, "Unable to perform the get operation because it is writable"); + }); + + it("should verify parse EXE output - errors - no conflicts", async function() { + let localPaths: string[] = ["/usr/alias/repo1"]; + let cmd: Sync = new Sync(undefined, localPaths, true); + let executionResult: IExecutionResult = { + exitCode: 1, + stdout: "/usr/alias/repo1:\n", + stderr: "new4.txt - Unable to perform the get operation because you have a conflicting rename (to be moved from /path/new5.txt)\n" + + "Warning new111.txt - Unable to perform the get operation because it is writable\n" + + "/usr/alias/repo1/folder cannot be deleted because it is not empty" + }; + + let results: ISyncResults = await cmd.ParseExeOutput(executionResult); + assert.equal(results.itemResults.length, 3); + assert.equal(results.hasConflicts, false); + assert.equal(results.hasErrors, true); + assert.equal(results.itemResults[0].syncType, SyncType.Error); + assert.equal(results.itemResults[0].itemPath, "new4.txt"); + assert.equal(results.itemResults[0].message, "Unable to perform the get operation because you have a conflicting rename (to be moved from /path/new5.txt)"); + assert.equal(results.itemResults[1].syncType, SyncType.Warning); + assert.equal(results.itemResults[1].itemPath, "new111.txt"); + assert.equal(results.itemResults[1].message, "Unable to perform the get operation because it is writable"); + assert.equal(results.itemResults[2].syncType, SyncType.Warning); + assert.equal(results.itemResults[2].itemPath, "/usr/alias/repo1/folder"); + assert.equal(results.itemResults[2].message, "/usr/alias/repo1/folder cannot be deleted because it is not empty"); + }); + + it("should verify parse EXE output - errors - exit code 100", async function() { + let localPaths: string[] = ["/usr/alias/repo 1"]; + let cmd: Sync = new Sync(undefined, localPaths, true); + let executionResult: IExecutionResult = { + exitCode: 100, + stdout: "Something bad this way comes.", + stderr: undefined + }; + + try { + await cmd.ParseExeOutput(executionResult); + } catch (err) { + assert.equal(err.exitCode, 100); + assert.equal(err.tfvcCommand, "get"); + assert.equal(err.message.indexOf(Strings.TfExecFailedError), 0); + assert.equal(err.stdout.indexOf("Something bad this way comes."), 0); + } + }); + }); diff --git a/test/tfvc/commands/undo.test.ts b/test/tfvc/commands/undo.test.ts index 1fd586f9e..79046b31b 100644 --- a/test/tfvc/commands/undo.test.ts +++ b/test/tfvc/commands/undo.test.ts @@ -69,6 +69,12 @@ describe("Tfvc-UndoCommand", function() { assert.deepEqual(cmd.GetOptions(), {}); }); + it("should verify GetExeOptions", function() { + let localPaths: string[] = ["/usr/alias/repos/Tfvc.L2VSCodeExtension.RC/README.md"]; + let cmd: Undo = new Undo(context, localPaths); + assert.deepEqual(cmd.GetExeOptions(), {}); + }); + it("should verify arguments", function() { let localPaths: string[] = ["/usr/alias/repos/Tfvc.L2VSCodeExtension.RC/README.md"]; let cmd: Undo = new Undo(undefined, localPaths); @@ -83,6 +89,20 @@ describe("Tfvc-UndoCommand", function() { assert.equal(cmd.GetArguments().GetArgumentsForDisplay(), "undo -noprompt -collection:" + collectionUrl + " ******** " + localPaths[0]); }); + it("should verify GetExeArguments", function() { + let localPaths: string[] = ["/usr/alias/repos/Tfvc.L2VSCodeExtension.RC/README.md"]; + let cmd: Undo = new Undo(undefined, localPaths); + + assert.equal(cmd.GetExeArguments().GetArgumentsForDisplay(), "undo -noprompt " + localPaths[0]); + }); + + it("should verify GetExeArguments with context", function() { + let localPaths: string[] = ["/usr/alias/repos/Tfvc.L2VSCodeExtension.RC/README.md"]; + let cmd: Undo = new Undo(context, localPaths); + + assert.equal(cmd.GetExeArguments().GetArgumentsForDisplay(), "undo -noprompt -collection:" + collectionUrl + " ******** " + localPaths[0]); + }); + it("should verify parse output - no output", async function() { let localPaths: string[] = ["/usr/alias/repos/Tfvc.L2VSCodeExtension.RC/README.md"]; let cmd: Undo = new Undo(undefined, localPaths); @@ -241,4 +261,165 @@ describe("Tfvc-UndoCommand", function() { } }); + /*********************************************************************************************** + * The methods below are duplicates of the parse output methods but call the parseExeOutput. + ***********************************************************************************************/ + + it("should verify parse EXE output - no output", async function() { + let localPaths: string[] = ["/usr/alias/repos/Tfvc.L2VSCodeExtension.RC/README.md"]; + let cmd: Undo = new Undo(undefined, localPaths); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: undefined, + stderr: undefined + }; + + let filesUndone: string[] = await cmd.ParseExeOutput(executionResult); + assert.equal(filesUndone.length, 0); + }); + + it("should verify parse EXE output - single file edit - no errors", async function() { + let localPaths: string[] = ["README.md"]; + let cmd: Undo = new Undo(undefined, localPaths); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: "Undoing edit: README.md\n", + stderr: undefined + }; + + let filesUndone: string[] = await cmd.ParseExeOutput(executionResult); + assert.equal(filesUndone.length, 1); + assert.equal(filesUndone[0], "README.md"); + }); + + it("should verify parse EXE output - single file add - no errors", async function() { + let localPaths: string[] = ["README.md"]; + let cmd: Undo = new Undo(undefined, localPaths); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: "Undoing add: README.md\n", + stderr: undefined + }; + + let filesUndone: string[] = await cmd.ParseExeOutput(executionResult); + assert.equal(filesUndone.length, 1); + assert.equal(filesUndone[0], "README.md"); + }); + + it("should verify parse EXE output - single folder+file edit - no errors", async function() { + let localPaths: string[] = [path.join("folder1", "file1.txt")]; + let cmd: Undo = new Undo(undefined, localPaths); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: "folder1:\n" + + "Undoing edit: file1.txt\n", + stderr: undefined + }; + + let filesUndone: string[] = await cmd.ParseExeOutput(executionResult); + assert.equal(filesUndone.length, 1); + assert.equal(filesUndone[0], localPaths[0]); + }); + + it("should verify parse EXE output - single subfolder+file add - no errors", async function() { + let localPaths: string[] = [path.join("folder1", "folder2", "file2.txt")]; + let cmd: Undo = new Undo(undefined, localPaths); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: path.join("folder1", "folder2") + ":\n" + + "Undoing edit: file2.txt\n", + stderr: undefined + }; + + let filesUndone: string[] = await cmd.ParseExeOutput(executionResult); + assert.equal(filesUndone.length, 1); + assert.equal(filesUndone[0], localPaths[0]); + }); + + it("should verify parse EXE output - single folder+file edit - spaces - no errors", async function() { + let localPaths: string[] = [path.join("fold er1", "file1.txt")]; + let cmd: Undo = new Undo(undefined, localPaths); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: "fold er1:\n" + + "Undoing edit: file1.txt\n", + stderr: undefined + }; + + let filesUndone: string[] = await cmd.ParseExeOutput(executionResult); + assert.equal(filesUndone.length, 1); + assert.equal(filesUndone[0], localPaths[0]); + }); + + it("should verify parse EXE output - single subfolder+file add - spaces - no errors", async function() { + let localPaths: string[] = [path.join("fold er1", "fol der2", "file2.txt")]; + let cmd: Undo = new Undo(undefined, localPaths); + let executionResult: IExecutionResult = { + exitCode: 0, + stdout: path.join("fold er1", "fol der2") + ":\n" + + "Undoing edit: file2.txt\n", + stderr: undefined + }; + + let filesUndone: string[] = await cmd.ParseExeOutput(executionResult); + assert.equal(filesUndone.length, 1); + assert.equal(filesUndone[0], localPaths[0]); + }); + + //If we have at least 1 file undone but at least 1 with no pending changes, exit code is 1 + //Proceed normally but ignore the files that have no pending changes. + it("should verify parse EXE output - multiple files - several no pending changes", async function() { + let noChangesPaths: string[] = [path.join("folder1", "file1.txt"), path.join("folder2", "file2.txt")]; + let localPaths: string[] = ["README.md"].concat(noChangesPaths); + let cmd: Undo = new Undo(undefined, localPaths); + let executionResult: IExecutionResult = { + exitCode: 1, + stdout: "Undoing add: README.md\n" + + "No pending changes were found for " + noChangesPaths[0] + ".\n" + + "No pending changes were found for " + noChangesPaths[1] + ".\n", + stderr: undefined + }; + + let filesUndone: string[] = await cmd.ParseExeOutput(executionResult); + assert.equal(filesUndone.length, 1); + assert.equal(filesUndone[0], "README.md"); + }); + + //If all files have no pending changes, exit code is 100 but we don't want to fail + it("should verify parse EXE output - multiple files - all no pending changes", async function() { + let noChangesPaths: string[] = [path.join("folder1", "file1.txt"), path.join("folder2", "file2.txt")]; + let localPaths: string[] = noChangesPaths; + let cmd: Undo = new Undo(undefined, localPaths); + let executionResult: IExecutionResult = { + exitCode: 100, + stdout: "" + + "No pending changes were found for " + noChangesPaths[0] + ".\n" + + "No pending changes were found for " + noChangesPaths[1] + ".\n", + stderr: undefined + }; + + let filesUndone: string[] = await cmd.ParseExeOutput(executionResult); + assert.isDefined(filesUndone); + assert.equal(filesUndone.length, 0); + }); + + it("should verify parse EXE output - error exit code", async function() { + let noChangesPaths: string[] = [path.join("folder1", "file1.txt"), path.join("folder2", "file2.txt")]; + let localPaths: string[] = noChangesPaths; + let cmd: Undo = new Undo(undefined, localPaths); + let executionResult: IExecutionResult = { + exitCode: 42, + stdout: "Something bad this way comes.", + stderr: undefined + }; + + try { + await cmd.ParseExeOutput(executionResult); + } catch (err) { + assert.equal(err.exitCode, 42); + assert.equal(err.tfvcCommand, "undo"); + assert.equal(err.message.indexOf(Strings.TfExecFailedError), 0); + assert.equal(err.stdout.indexOf("Something bad this way comes."), 0); + } + }); });