import { parse } from '../diff-parser'; describe('DiffParser', () => { describe('generateDiffJson', () => { // eslint-disable-next-line jest/expect-expect it('should parse unix with \n diff', () => { const diff = 'diff --git a/sample b/sample\n' + 'index 0000001..0ddf2ba\n' + '--- a/sample\n' + '+++ b/sample\n' + '@@ -1 +1 @@\n' + '-test\n' + '+test1r\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -1 +1 @@", "lines": Array [ Object { "content": "-test", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "+test1r", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "0ddf2ba", "checksumBefore": "0000001", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "language": undefined, "newName": "sample", "oldName": "sample", }, ] `); }); // eslint-disable-next-line jest/expect-expect it('should parse windows with \r\n diff', () => { const diff = 'diff --git a/sample b/sample\r\n' + 'index 0000001..0ddf2ba\r\n' + '--- a/sample\r\n' + '+++ b/sample\r\n' + '@@ -1 +1 @@\r\n' + '-test\r\n' + '+test1r\r\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -1 +1 @@", "lines": Array [ Object { "content": "-test", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "+test1r", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "0ddf2ba", "checksumBefore": "0000001", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "language": undefined, "newName": "sample", "oldName": "sample", }, ] `); }); // eslint-disable-next-line jest/expect-expect it('should parse old os x with \r diff', () => { const diff = 'diff --git a/sample b/sample\r' + 'index 0000001..0ddf2ba\r' + '--- a/sample\r' + '+++ b/sample\r' + '@@ -1 +1 @@\r' + '-test\r' + '+test1r\r'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -1 +1 @@", "lines": Array [ Object { "content": "-test", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "+test1r", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "0ddf2ba", "checksumBefore": "0000001", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "language": undefined, "newName": "sample", "oldName": "sample", }, ] `); }); // eslint-disable-next-line jest/expect-expect it('should parse mixed eols diff', () => { const diff = 'diff --git a/sample b/sample\n' + 'index 0000001..0ddf2ba\r\n' + '--- a/sample\r' + '+++ b/sample\r\n' + '@@ -1 +1 @@\n' + '-test\r' + '+test1r\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -1 +1 @@", "lines": Array [ Object { "content": "-test", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "+test1r", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "0ddf2ba", "checksumBefore": "0000001", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "language": undefined, "newName": "sample", "oldName": "sample", }, ] `); }); it('should parse diff with special characters', () => { const diff = 'diff --git "a/bla with \ttab.scala" "b/bla with \ttab.scala"\n' + 'index 4c679d7..e9bd385 100644\n' + '--- "a/bla with \ttab.scala"\n' + '+++ "b/bla with \ttab.scala"\n' + '@@ -1 +1,2 @@\n' + '-cenas\n' + '+cenas com ananas\n' + '+bananas'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 2, "blocks": Array [ Object { "header": "@@ -1 +1,2 @@", "lines": Array [ Object { "content": "-cenas", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "+cenas com ananas", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, Object { "content": "+bananas", "newNumber": 2, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "e9bd385", "checksumBefore": "4c679d7", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "language": "scala", "mode": "100644", "newName": "bla with tab.scala", "oldName": "bla with tab.scala", }, ] `); }); it('should parse diff with prefix', () => { const diff = 'diff --git "\tbla with \ttab.scala" "\tbla with \ttab.scala"\n' + 'index 4c679d7..e9bd385 100644\n' + '--- "\tbla with \ttab.scala"\n' + '+++ "\tbla with \ttab.scala"\n' + '@@ -1 +1,2 @@\n' + '-cenas\n' + '+cenas com ananas\n' + '+bananas'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 2, "blocks": Array [ Object { "header": "@@ -1 +1,2 @@", "lines": Array [ Object { "content": "-cenas", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "+cenas com ananas", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, Object { "content": "+bananas", "newNumber": 2, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "e9bd385", "checksumBefore": "4c679d7", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "language": "scala", "mode": "100644", "newName": " bla with tab.scala", "oldName": " bla with tab.scala", }, ] `); }); it('should parse diff with deleted file', () => { const diff = 'diff --git a/src/var/strundefined.js b/src/var/strundefined.js\n' + 'deleted file mode 100644\n' + 'index 04e16b0..0000000\n' + '--- a/src/var/strundefined.js\n' + '+++ /dev/null\n' + '@@ -1,3 +0,0 @@\n' + '-define(() => {\n' + '- return typeof undefined;\n' + '-});\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 0, "blocks": Array [ Object { "header": "@@ -1,3 +0,0 @@", "lines": Array [ Object { "content": "-define(() => {", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "- return typeof undefined;", "newNumber": undefined, "oldNumber": 2, "type": "delete", }, Object { "content": "-});", "newNumber": undefined, "oldNumber": 3, "type": "delete", }, ], "newStartLine": 0, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "0000000", "checksumBefore": "04e16b0", "deletedFileMode": "100644", "deletedLines": 3, "isCombined": false, "isDeleted": true, "isGitDiff": true, "language": "js", "newName": "/dev/null", "oldName": "src/var/strundefined.js", }, ] `); }); it('should parse diff with new file', () => { const diff = 'diff --git a/test.js b/test.js\n' + 'new file mode 100644\n' + 'index 0000000..e1e22ec\n' + '--- /dev/null\n' + '+++ b/test.js\n' + '@@ -0,0 +1,5 @@\n' + "+var parser = require('./source/git-parser');\n" + '+\n' + '+var patchLineList = [ false, false, false, false ];\n' + '+\n' + '+console.log(parser.parsePatchDiffResult(text, patchLineList));\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 5, "blocks": Array [ Object { "header": "@@ -0,0 +1,5 @@", "lines": Array [ Object { "content": "+var parser = require('./source/git-parser');", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, Object { "content": "+", "newNumber": 2, "oldNumber": undefined, "type": "insert", }, Object { "content": "+var patchLineList = [ false, false, false, false ];", "newNumber": 3, "oldNumber": undefined, "type": "insert", }, Object { "content": "+", "newNumber": 4, "oldNumber": undefined, "type": "insert", }, Object { "content": "+console.log(parser.parsePatchDiffResult(text, patchLineList));", "newNumber": 5, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 0, "oldStartLine2": null, }, ], "checksumAfter": "e1e22ec", "checksumBefore": "0000000", "deletedLines": 0, "isCombined": false, "isGitDiff": true, "isNew": true, "language": "js", "newFileMode": "100644", "newName": "test.js", "oldName": "/dev/null", }, ] `); }); it('should parse diff with nested diff', () => { const diff = 'diff --git a/src/offset.js b/src/offset.js\n' + 'index cc6ffb4..fa51f18 100644\n' + '--- a/src/offset.js\n' + '+++ b/src/offset.js\n' + '@@ -1,6 +1,5 @@\n' + "+var parser = require('./source/git-parser');\n" + '+\n' + "+var text = 'diff --git a/components/app/app.html b/components/app/app.html\\nindex ecb7a95..027bd9b 100644\\n--- a/components/app/app.html\\n+++ b/components/app/app.html\\n@@ -52,0 +53,3 @@\\n+\\n+\\n+\\n@@ -56,0 +60,3 @@\\n+\\n+\\n+\\n'\n" + '+var patchLineList = [ false, false, false, false ];\n' + '+\n' + '+console.log(parser.parsePatchDiffResult(text, patchLineList));\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 6, "blocks": Array [ Object { "header": "@@ -1,6 +1,5 @@", "lines": Array [ Object { "content": "+var parser = require('./source/git-parser');", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, Object { "content": "+", "newNumber": 2, "oldNumber": undefined, "type": "insert", }, Object { "content": "+var text = 'diff --git a/components/app/app.html b/components/app/app.html\\\\nindex ecb7a95..027bd9b 100644\\\\n--- a/components/app/app.html\\\\n+++ b/components/app/app.html\\\\n@@ -52,0 +53,3 @@\\\\n+\\\\n+\\\\n+\\\\n@@ -56,0 +60,3 @@\\\\n+\\\\n+\\\\n+\\\\n'", "newNumber": 3, "oldNumber": undefined, "type": "insert", }, Object { "content": "+var patchLineList = [ false, false, false, false ];", "newNumber": 4, "oldNumber": undefined, "type": "insert", }, Object { "content": "+", "newNumber": 5, "oldNumber": undefined, "type": "insert", }, Object { "content": "+console.log(parser.parsePatchDiffResult(text, patchLineList));", "newNumber": 6, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "fa51f18", "checksumBefore": "cc6ffb4", "deletedLines": 0, "isCombined": false, "isGitDiff": true, "language": "js", "mode": "100644", "newName": "src/offset.js", "oldName": "src/offset.js", }, ] `); }); it('should parse diff with multiple blocks', () => { const diff = 'diff --git a/src/attributes/classes.js b/src/attributes/classes.js\n' + 'index c617824..c8d1393 100644\n' + '--- a/src/attributes/classes.js\n' + '+++ b/src/attributes/classes.js\n' + '@@ -1,10 +1,9 @@\n' + ' define([\n' + ' "../core",\n' + ' "../var/rnotwhite",\n' + '- "../var/strundefined",\n' + ' "../data/var/dataPriv",\n' + ' "../core/init"\n' + '-], function( jQuery, rnotwhite, strundefined, dataPriv ) {\n' + '+], function( jQuery, rnotwhite, dataPriv ) {\n' + ' \n' + ' var rclass = /[\\t\\r\\n\\f]/g;\n' + ' \n' + '@@ -128,7 +127,7 @@ jQuery.fn.extend({\n' + ' }\n' + ' \n' + ' // Toggle whole class name\n' + '- } else if ( type === strundefined || type === "boolean" ) {\n' + '+ } else if ( value === undefined || type === "boolean" ) {\n' + ' if ( this.className ) {\n' + ' // store className if set\n' + ' dataPriv.set( this, "__className__", this.className );\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 2, "blocks": Array [ Object { "header": "@@ -1,10 +1,9 @@", "lines": Array [ Object { "content": " define([", "newNumber": 1, "oldNumber": 1, "type": "context", }, Object { "content": " \\"../core\\",", "newNumber": 2, "oldNumber": 2, "type": "context", }, Object { "content": " \\"../var/rnotwhite\\",", "newNumber": 3, "oldNumber": 3, "type": "context", }, Object { "content": "- \\"../var/strundefined\\",", "newNumber": undefined, "oldNumber": 4, "type": "delete", }, Object { "content": " \\"../data/var/dataPriv\\",", "newNumber": 4, "oldNumber": 5, "type": "context", }, Object { "content": " \\"../core/init\\"", "newNumber": 5, "oldNumber": 6, "type": "context", }, Object { "content": "-], function( jQuery, rnotwhite, strundefined, dataPriv ) {", "newNumber": undefined, "oldNumber": 7, "type": "delete", }, Object { "content": "+], function( jQuery, rnotwhite, dataPriv ) {", "newNumber": 6, "oldNumber": undefined, "type": "insert", }, Object { "content": " ", "newNumber": 7, "oldNumber": 8, "type": "context", }, Object { "content": " var rclass = /[\\\\t\\\\r\\\\n\\\\f]/g;", "newNumber": 8, "oldNumber": 9, "type": "context", }, Object { "content": " ", "newNumber": 9, "oldNumber": 10, "type": "context", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, Object { "header": "@@ -128,7 +127,7 @@ jQuery.fn.extend({", "lines": Array [ Object { "content": " }", "newNumber": 127, "oldNumber": 128, "type": "context", }, Object { "content": " ", "newNumber": 128, "oldNumber": 129, "type": "context", }, Object { "content": " // Toggle whole class name", "newNumber": 129, "oldNumber": 130, "type": "context", }, Object { "content": "- } else if ( type === strundefined || type === \\"boolean\\" ) {", "newNumber": undefined, "oldNumber": 131, "type": "delete", }, Object { "content": "+ } else if ( value === undefined || type === \\"boolean\\" ) {", "newNumber": 130, "oldNumber": undefined, "type": "insert", }, Object { "content": " if ( this.className ) {", "newNumber": 131, "oldNumber": 132, "type": "context", }, Object { "content": " // store className if set", "newNumber": 132, "oldNumber": 133, "type": "context", }, Object { "content": " dataPriv.set( this, \\"__className__\\", this.className );", "newNumber": 133, "oldNumber": 134, "type": "context", }, ], "newStartLine": 127, "oldStartLine": 128, "oldStartLine2": null, }, ], "checksumAfter": "c8d1393", "checksumBefore": "c617824", "deletedLines": 3, "isCombined": false, "isGitDiff": true, "language": "js", "mode": "100644", "newName": "src/attributes/classes.js", "oldName": "src/attributes/classes.js", }, ] `); }); it('should parse diff with multiple files', () => { const diff = 'diff --git a/src/core/init.js b/src/core/init.js\n' + 'index e49196a..50f310c 100644\n' + '--- a/src/core/init.js\n' + '+++ b/src/core/init.js\n' + '@@ -101,7 +101,7 @@ var rootjQuery,\n' + ' // HANDLE: $(function)\n' + ' // Shortcut for document ready\n' + ' } else if ( jQuery.isFunction( selector ) ) {\n' + '- return typeof rootjQuery.ready !== "undefined" ?\n' + '+ return rootjQuery.ready !== undefined ?\n' + ' rootjQuery.ready( selector ) :\n' + ' // Execute immediately if ready is not present\n' + ' selector( jQuery );\n' + 'diff --git a/src/event.js b/src/event.js\n' + 'index 7336f4d..6183f70 100644\n' + '--- a/src/event.js\n' + '+++ b/src/event.js\n' + '@@ -1,6 +1,5 @@\n' + ' define([\n' + ' "./core",\n' + '- "./var/strundefined",\n' + ' "./var/rnotwhite",\n' + ' "./var/hasOwn",\n' + ' "./var/slice",\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -101,7 +101,7 @@ var rootjQuery,", "lines": Array [ Object { "content": " // HANDLE: $(function)", "newNumber": 101, "oldNumber": 101, "type": "context", }, Object { "content": " // Shortcut for document ready", "newNumber": 102, "oldNumber": 102, "type": "context", }, Object { "content": " } else if ( jQuery.isFunction( selector ) ) {", "newNumber": 103, "oldNumber": 103, "type": "context", }, Object { "content": "- return typeof rootjQuery.ready !== \\"undefined\\" ?", "newNumber": undefined, "oldNumber": 104, "type": "delete", }, Object { "content": "+ return rootjQuery.ready !== undefined ?", "newNumber": 104, "oldNumber": undefined, "type": "insert", }, Object { "content": " rootjQuery.ready( selector ) :", "newNumber": 105, "oldNumber": 105, "type": "context", }, Object { "content": " // Execute immediately if ready is not present", "newNumber": 106, "oldNumber": 106, "type": "context", }, Object { "content": " selector( jQuery );", "newNumber": 107, "oldNumber": 107, "type": "context", }, ], "newStartLine": 101, "oldStartLine": 101, "oldStartLine2": null, }, ], "checksumAfter": "50f310c", "checksumBefore": "e49196a", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "language": "js", "mode": "100644", "newName": "src/core/init.js", "oldName": "src/core/init.js", }, Object { "addedLines": 0, "blocks": Array [ Object { "header": "@@ -1,6 +1,5 @@", "lines": Array [ Object { "content": " define([", "newNumber": 1, "oldNumber": 1, "type": "context", }, Object { "content": " \\"./core\\",", "newNumber": 2, "oldNumber": 2, "type": "context", }, Object { "content": "- \\"./var/strundefined\\",", "newNumber": undefined, "oldNumber": 3, "type": "delete", }, Object { "content": " \\"./var/rnotwhite\\",", "newNumber": 3, "oldNumber": 4, "type": "context", }, Object { "content": " \\"./var/hasOwn\\",", "newNumber": 4, "oldNumber": 5, "type": "context", }, Object { "content": " \\"./var/slice\\",", "newNumber": 5, "oldNumber": 6, "type": "context", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "6183f70", "checksumBefore": "7336f4d", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "language": "js", "mode": "100644", "newName": "src/event.js", "oldName": "src/event.js", }, ] `); }); it('should parse combined diff', () => { const diff = 'diff --combined describe.c\n' + 'index fabadb8,cc95eb0..4866510\n' + '--- a/describe.c\n' + '+++ b/describe.c\n' + '@@@ -98,20 -98,12 +98,20 @@@\n' + ' return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;\n' + ' }\n' + ' \n' + '- static void describe(char *arg)\n' + ' -static void describe(struct commit *cmit, int last_one)\n' + '++static void describe(char *arg, int last_one)\n' + ' {\n' + ' + unsigned char sha1[20];\n' + ' + struct commit *cmit;\n' + ' struct commit_list *list;\n' + ' static int initialized = 0;\n' + ' struct commit_name *n;\n' + ' \n' + ' + if (get_sha1(arg, sha1) < 0)\n' + ' + usage(describe_usage);\n' + ' + cmit = lookup_commit_reference(sha1);\n' + ' + if (!cmit)\n' + ' + usage(describe_usage);\n' + ' +\n' + ' if (!initialized) {\n' + ' initialized = 1;\n' + ' for_each_ref(get_name);\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 9, "blocks": Array [ Object { "header": "@@@ -98,20 -98,12 +98,20 @@@", "lines": Array [ Object { "content": " return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;", "newNumber": 98, "oldNumber": 98, "type": "context", }, Object { "content": " }", "newNumber": 99, "oldNumber": 99, "type": "context", }, Object { "content": " ", "newNumber": 100, "oldNumber": 100, "type": "context", }, Object { "content": "- static void describe(char *arg)", "newNumber": undefined, "oldNumber": 101, "type": "delete", }, Object { "content": " -static void describe(struct commit *cmit, int last_one)", "newNumber": undefined, "oldNumber": 102, "type": "delete", }, Object { "content": "++static void describe(char *arg, int last_one)", "newNumber": 101, "oldNumber": undefined, "type": "insert", }, Object { "content": " {", "newNumber": 102, "oldNumber": 103, "type": "context", }, Object { "content": " + unsigned char sha1[20];", "newNumber": 103, "oldNumber": undefined, "type": "insert", }, Object { "content": " + struct commit *cmit;", "newNumber": 104, "oldNumber": undefined, "type": "insert", }, Object { "content": " struct commit_list *list;", "newNumber": 105, "oldNumber": 104, "type": "context", }, Object { "content": " static int initialized = 0;", "newNumber": 106, "oldNumber": 105, "type": "context", }, Object { "content": " struct commit_name *n;", "newNumber": 107, "oldNumber": 106, "type": "context", }, Object { "content": " ", "newNumber": 108, "oldNumber": 107, "type": "context", }, Object { "content": " + if (get_sha1(arg, sha1) < 0)", "newNumber": 109, "oldNumber": undefined, "type": "insert", }, Object { "content": " + usage(describe_usage);", "newNumber": 110, "oldNumber": undefined, "type": "insert", }, Object { "content": " + cmit = lookup_commit_reference(sha1);", "newNumber": 111, "oldNumber": undefined, "type": "insert", }, Object { "content": " + if (!cmit)", "newNumber": 112, "oldNumber": undefined, "type": "insert", }, Object { "content": " + usage(describe_usage);", "newNumber": 113, "oldNumber": undefined, "type": "insert", }, Object { "content": " +", "newNumber": 114, "oldNumber": undefined, "type": "insert", }, Object { "content": " if (!initialized) {", "newNumber": 115, "oldNumber": 108, "type": "context", }, Object { "content": " initialized = 1;", "newNumber": 116, "oldNumber": 109, "type": "context", }, Object { "content": " for_each_ref(get_name);", "newNumber": 117, "oldNumber": 110, "type": "context", }, ], "newStartLine": 98, "oldStartLine": 98, "oldStartLine2": 98, }, ], "checksumAfter": "fabadb8", "checksumBefore": Array [ "cc95eb0", "4866510", ], "deletedLines": 2, "isCombined": true, "isGitDiff": true, "language": "c", "newName": "describe.c", "oldName": "describe.c", }, ] `); }); it('should parse diffs with copied files', () => { const diff = 'diff --git a/index.js b/more-index.js\n' + 'dissimilarity index 5%\n' + 'copy from index.js\n' + 'copy to more-index.js\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 0, "blocks": Array [], "changedPercentage": 5, "deletedLines": 0, "isCopy": true, "isGitDiff": true, "newName": "more-index.js", "oldName": "index.js", }, ] `); }); it('should parse diffs with moved files', () => { const diff = 'diff --git a/more-index.js b/other-index.js\n' + 'similarity index 86%\n' + 'rename from more-index.js\n' + 'rename to other-index.js\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 0, "blocks": Array [], "deletedLines": 0, "isGitDiff": true, "isRename": true, "newName": "other-index.js", "oldName": "more-index.js", "unchangedPercentage": 86, }, ] `); }); it('should parse diffs correct line numbers', () => { const diff = 'diff --git a/sample b/sample\n' + 'index 0000001..0ddf2ba\n' + '--- a/sample\n' + '+++ b/sample\n' + '@@ -1 +1,2 @@\n' + '-test\n' + '+test1r\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -1 +1,2 @@", "lines": Array [ Object { "content": "-test", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "+test1r", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "0ddf2ba", "checksumBefore": "0000001", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "language": undefined, "newName": "sample", "oldName": "sample", }, ] `); }); it('should parse unified non git diff and strip timestamps off the headers', () => { const diffs = [ // 2 hours ahead of GMT '--- a/sample.js 2016-10-25 11:37:14.000000000 +0200\n' + '+++ b/sample.js 2016-10-25 11:37:14.000000000 +0200\n' + '@@ -1 +1,2 @@\n' + '-test\n' + '+test1r\n' + '+test2r', // 2 hours behind GMT '--- a/sample.js 2016-10-25 11:37:14.000000000 -0200\n' + '+++ b/sample.js 2016-10-25 11:37:14.000000000 -0200\n' + '@@ -1 +1,2 @@\n' + '-test\n' + '+test1r\n' + '+test2r', ].join('\n'); const result = parse(diffs); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 2, "blocks": Array [ Object { "header": "@@ -1 +1,2 @@", "lines": Array [ Object { "content": "-test", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "+test1r", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, Object { "content": "+test2r", "newNumber": 2, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "deletedLines": 1, "isCombined": false, "language": "js", "newName": "sample.js", "oldName": "sample.js", }, Object { "addedLines": 2, "blocks": Array [ Object { "header": "@@ -1 +1,2 @@", "lines": Array [ Object { "content": "-test", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "+test1r", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, Object { "content": "+test2r", "newNumber": 2, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "deletedLines": 1, "isCombined": false, "language": "js", "newName": "sample.js", "oldName": "sample.js", }, ] `); }); it('should parse unified non git diff', () => { const diff = '--- a/sample.js\n' + '+++ b/sample.js\n' + '@@ -1 +1,2 @@\n' + '-test\n' + '+test1r\n' + '+test2r\n'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 2, "blocks": Array [ Object { "header": "@@ -1 +1,2 @@", "lines": Array [ Object { "content": "-test", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, Object { "content": "+test1r", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, Object { "content": "+test2r", "newNumber": 2, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "deletedLines": 1, "isCombined": false, "language": "js", "newName": "sample.js", "oldName": "sample.js", }, ] `); }); it('should parse unified diff with multiple hunks and files', () => { const diff = '--- sample.js\n' + '+++ sample.js\n' + '@@ -1 +1,2 @@\n' + '-test\n' + '@@ -10 +20,2 @@\n' + '+test\n' + '--- sample1.js\n' + '+++ sample1.js\n' + '@@ -1 +1,2 @@\n' + '+test1'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -1 +1,2 @@", "lines": Array [ Object { "content": "-test", "newNumber": undefined, "oldNumber": 1, "type": "delete", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, Object { "header": "@@ -10 +20,2 @@", "lines": Array [ Object { "content": "+test", "newNumber": 20, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 20, "oldStartLine": 10, "oldStartLine2": null, }, ], "deletedLines": 1, "isCombined": false, "language": "js", "newName": "sample.js", "oldName": "sample.js", }, Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -1 +1,2 @@", "lines": Array [ Object { "content": "+test1", "newNumber": 1, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "deletedLines": 0, "isCombined": false, "language": "js", "newName": "sample1.js", "oldName": "sample1.js", }, ] `); }); it('should parse diff with --- and +++ in the context lines', () => { const diff = '--- sample.js\n' + '+++ sample.js\n' + '@@ -1,8 +1,8 @@\n' + ' test\n' + ' \n' + '-- 1\n' + '--- 1\n' + '---- 1\n' + ' \n' + '++ 2\n' + '+++ 2\n' + '++++ 2'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 3, "blocks": Array [ Object { "header": "@@ -1,8 +1,8 @@", "lines": Array [ Object { "content": " test", "newNumber": 1, "oldNumber": 1, "type": "context", }, Object { "content": " ", "newNumber": 2, "oldNumber": 2, "type": "context", }, Object { "content": "-- 1", "newNumber": undefined, "oldNumber": 3, "type": "delete", }, Object { "content": "--- 1", "newNumber": undefined, "oldNumber": 4, "type": "delete", }, Object { "content": "---- 1", "newNumber": undefined, "oldNumber": 5, "type": "delete", }, Object { "content": " ", "newNumber": 3, "oldNumber": 6, "type": "context", }, Object { "content": "++ 2", "newNumber": 4, "oldNumber": undefined, "type": "insert", }, Object { "content": "+++ 2", "newNumber": 5, "oldNumber": undefined, "type": "insert", }, Object { "content": "++++ 2", "newNumber": 6, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "deletedLines": 3, "isCombined": false, "language": "js", "newName": "sample.js", "oldName": "sample.js", }, ] `); }); it('should parse diff without proper hunk headers', () => { const diff = '--- sample.js\n' + '+++ sample.js\n' + '@@ @@\n' + ' test'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 0, "blocks": Array [ Object { "header": "@@ @@", "lines": Array [ Object { "content": " test", "newNumber": 0, "oldNumber": 0, "type": "context", }, ], "newStartLine": 0, "oldStartLine": 0, "oldStartLine2": null, }, ], "deletedLines": 0, "isCombined": false, "language": "js", "newName": "sample.js", "oldName": "sample.js", }, ] `); }); it('should parse binary file diff', () => { const diff = 'diff --git a/last-changes-config.png b/last-changes-config.png\n' + 'index 322248b..56fc1f2 100644\n' + '--- a/last-changes-config.png\n' + '+++ b/last-changes-config.png\n' + 'Binary files differ'; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 0, "blocks": Array [ Object { "header": "Binary files differ", "lines": Array [], "newStartLine": 0, "oldStartLine": 0, "oldStartLine2": null, }, ], "checksumAfter": "56fc1f2", "checksumBefore": "322248b", "deletedLines": 0, "isCombined": false, "isGitDiff": true, "language": "png", "mode": "100644", "newName": "last-changes-config.png", "oldName": "last-changes-config.png", }, ] `); }); it('should parse diff with --find-renames', () => { const diff = 'diff --git a/src/test-bar.js b/src/test-baz.js\n' + 'similarity index 98%\n' + 'rename from src/test-bar.js\n' + 'rename to src/test-baz.js\n' + 'index e01513b..f14a870 100644\n' + '--- a/src/test-bar.js\n' + '+++ b/src/test-baz.js\n' + '@@ -1,4 +1,32 @@\n' + ' function foo() {\n' + '-var bar = "Whoops!";\n' + '+var baz = "Whoops!";\n' + ' }\n' + ' '; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -1,4 +1,32 @@", "lines": Array [ Object { "content": " function foo() {", "newNumber": 1, "oldNumber": 1, "type": "context", }, Object { "content": "-var bar = \\"Whoops!\\";", "newNumber": undefined, "oldNumber": 2, "type": "delete", }, Object { "content": "+var baz = \\"Whoops!\\";", "newNumber": 2, "oldNumber": undefined, "type": "insert", }, Object { "content": " }", "newNumber": 3, "oldNumber": 3, "type": "context", }, Object { "content": " ", "newNumber": 4, "oldNumber": 4, "type": "context", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "f14a870", "checksumBefore": "e01513b", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "isRename": true, "language": "js", "mode": "100644", "newName": "src/test-baz.js", "oldName": "src/test-bar.js", "unchangedPercentage": 98, }, ] `); }); it('should parse diff with prefix 2', () => { const diff = 'diff --git "\tTest.scala" "\tScalaTest.scala"\n' + 'similarity index 88%\n' + 'rename from Test.scala\n' + 'rename to ScalaTest.scala\n' + 'index 7d1f9bf..8b13271 100644\n' + '--- "\tTest.scala"\n' + '+++ "\tScalaTest.scala"\n' + '@@ -1,6 +1,8 @@\n' + ' class Test {\n' + ' \n' + ' def method1 = ???\n' + '+\n' + '+ def method2 = ???\n' + ' \n' + ' def myMethod = ???\n' + ' \n' + '@@ -10,7 +12,6 @@ class Test {\n' + ' \n' + ' def + = ???\n' + ' \n' + '- def |> = ???\n' + ' \n' + ' }\n' + ' \n' + 'diff --git "\ttardis.png" "\ttardis.png"\n' + 'new file mode 100644\n' + 'index 0000000..d503a29\n' + 'Binary files /dev/null and "\ttardis.png" differ\n' + 'diff --git a/src/test-bar.js b/src/test-baz.js\n' + 'similarity index 98%\n' + 'rename from src/test-bar.js\n' + 'rename to src/test-baz.js\n' + 'index e01513b..f14a870 100644\n' + '--- a/src/test-bar.js\n' + '+++ b/src/test-baz.js\n' + '@@ -1,4 +1,32 @@\n' + ' function foo() {\n' + '-var bar = "Whoops!";\n' + '+var baz = "Whoops!";\n' + ' }\n' + ' '; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 2, "blocks": Array [ Object { "header": "@@ -1,6 +1,8 @@", "lines": Array [ Object { "content": " class Test {", "newNumber": 1, "oldNumber": 1, "type": "context", }, Object { "content": " ", "newNumber": 2, "oldNumber": 2, "type": "context", }, Object { "content": " def method1 = ???", "newNumber": 3, "oldNumber": 3, "type": "context", }, Object { "content": "+", "newNumber": 4, "oldNumber": undefined, "type": "insert", }, Object { "content": "+ def method2 = ???", "newNumber": 5, "oldNumber": undefined, "type": "insert", }, Object { "content": " ", "newNumber": 6, "oldNumber": 4, "type": "context", }, Object { "content": " def myMethod = ???", "newNumber": 7, "oldNumber": 5, "type": "context", }, Object { "content": " ", "newNumber": 8, "oldNumber": 6, "type": "context", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, Object { "header": "@@ -10,7 +12,6 @@ class Test {", "lines": Array [ Object { "content": " ", "newNumber": 12, "oldNumber": 10, "type": "context", }, Object { "content": " def + = ???", "newNumber": 13, "oldNumber": 11, "type": "context", }, Object { "content": " ", "newNumber": 14, "oldNumber": 12, "type": "context", }, Object { "content": "- def |> = ???", "newNumber": undefined, "oldNumber": 13, "type": "delete", }, Object { "content": " ", "newNumber": 15, "oldNumber": 14, "type": "context", }, Object { "content": " }", "newNumber": 16, "oldNumber": 15, "type": "context", }, Object { "content": " ", "newNumber": 17, "oldNumber": 16, "type": "context", }, ], "newStartLine": 12, "oldStartLine": 10, "oldStartLine2": null, }, ], "checksumAfter": "8b13271", "checksumBefore": "7d1f9bf", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "isRename": true, "language": "scala", "mode": "100644", "newName": " ScalaTest.scala", "oldName": " Test.scala", "unchangedPercentage": 88, }, Object { "addedLines": 0, "blocks": Array [ Object { "header": "Binary file", "lines": Array [], "newStartLine": 0, "oldStartLine": 0, "oldStartLine2": null, }, ], "checksumAfter": "d503a29", "checksumBefore": "0000000", "deletedLines": 0, "isBinary": true, "isCombined": false, "isGitDiff": true, "isNew": true, "newFileMode": "100644", "newName": " tardis.png", "oldName": "/dev/null", }, Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -1,4 +1,32 @@", "lines": Array [ Object { "content": " function foo() {", "newNumber": 1, "oldNumber": 1, "type": "context", }, Object { "content": "-var bar = \\"Whoops!\\";", "newNumber": undefined, "oldNumber": 2, "type": "delete", }, Object { "content": "+var baz = \\"Whoops!\\";", "newNumber": 2, "oldNumber": undefined, "type": "insert", }, Object { "content": " }", "newNumber": 3, "oldNumber": 3, "type": "context", }, Object { "content": " ", "newNumber": 4, "oldNumber": 4, "type": "context", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "f14a870", "checksumBefore": "e01513b", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "isRename": true, "language": "js", "mode": "100644", "newName": "src/test-baz.js", "oldName": "src/test-bar.js", "unchangedPercentage": 98, }, ] `); }); it('should parse binary with content', () => { const diff = 'diff --git a/favicon.png b/favicon.png\n' + 'deleted file mode 100644\n' + 'index 2a9d516a5647205d7be510dd0dff93a3663eff6f..0000000000000000000000000000000000000000\n' + 'GIT binary patch\n' + 'literal 0\n' + 'HcmV?d00001\n' + '\n' + 'literal 471\n' + 'zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`EX7WqAsj$Z!;#Vf4nJ\n' + 'za0`JjqPVOAS4ANVKzqmCp=Cty@U^(7zk!jHsvT~YI{F^=Ex6g|gox78w\n' + 'z+Sn2Du3GS9U7qU`1*NYYlJi3u-!-_B#8k9H0\n' + 'zGl{FnZs<2$wz5^=Q2h-1XI^s{LQL1#T4epqNPC%Orl(tD_@!*EY++~^Lt2<2&!&%=\n' + 'z`m>(TYj6uS7jDdt=eH>iOyQg(QMR<-Fw8)Dk^ZG)XQTuzEgl{`GpS?Cfq9818R9~=\n' + 'z{&h9@9n8F^?|qusoPy{k#%tVHzu7H$t26CR`BJZk*Ixf&u36WuS=?6m2^ho-p00i_\n' + 'I>zopr0Nz-&lmGw#\n' + 'diff --git a/src/test-bar.js b/src/test-baz.js\n' + 'similarity index 98%\n' + 'rename from src/test-bar.js\n' + 'rename to src/test-baz.js\n' + 'index e01513b..f14a870 100644\n' + '--- a/src/test-bar.js\n' + '+++ b/src/test-baz.js\n' + '@@ -1,4 +1,32 @@\n' + ' function foo() {\n' + '-var bar = "Whoops!";\n' + '+var baz = "Whoops!";\n' + ' }\n' + ' '; const result = parse(diff); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 0, "blocks": Array [ Object { "header": "GIT binary patch", "lines": Array [], "newStartLine": 0, "oldStartLine": 0, "oldStartLine2": null, }, ], "checksumAfter": "0000000000000000000000000000000000000000", "checksumBefore": "2a9d516a5647205d7be510dd0dff93a3663eff6f", "deletedFileMode": "100644", "deletedLines": 0, "isBinary": true, "isCombined": false, "isDeleted": true, "isGitDiff": true, "newName": "favicon.png", "oldName": "favicon.png", }, Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -1,4 +1,32 @@", "lines": Array [ Object { "content": " function foo() {", "newNumber": 1, "oldNumber": 1, "type": "context", }, Object { "content": "-var bar = \\"Whoops!\\";", "newNumber": undefined, "oldNumber": 2, "type": "delete", }, Object { "content": "+var baz = \\"Whoops!\\";", "newNumber": 2, "oldNumber": undefined, "type": "insert", }, Object { "content": " }", "newNumber": 3, "oldNumber": 3, "type": "context", }, Object { "content": " ", "newNumber": 4, "oldNumber": 4, "type": "context", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "f14a870", "checksumBefore": "e01513b", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "isRename": true, "language": "js", "mode": "100644", "newName": "src/test-baz.js", "oldName": "src/test-bar.js", "unchangedPercentage": 98, }, ] `); }); it('should stop parsing file and mark it as `isTooBig` if `diffMaxChanges` is set and excedeed', () => { const diff = 'diff --git a/src/core/init.js b/src/core/init.js\n' + 'index e49196a..50f310c 100644\n' + '--- a/src/core/init.js\n' + '+++ b/src/core/init.js\n' + '@@ -101,7 +101,7 @@ var rootjQuery,\n' + ' // HANDLE: $(function)\n' + ' // Shortcut for document ready\n' + ' } else if ( jQuery.isFunction( selector ) ) {\n' + '- return typeof rootjQuery.ready !== "undefined" ?\n' + '+ return rootjQuery.ready !== undefined ?\n' + ' rootjQuery.ready( selector ) :\n' + ' // Execute immediately if ready is not present\n' + ' selector( jQuery );\n' + 'diff --git a/src/event.js b/src/event.js\n' + 'index 7336f4d..6183f70 100644\n' + '--- a/src/event.js\n' + '+++ b/src/event.js\n' + '@@ -1,6 +1,5 @@\n' + ' define([\n' + ' "./core",\n' + '- "./var/strundefined",\n' + ' "./var/rnotwhite",\n' + ' "./var/hasOwn",\n' + ' "./var/slice",\n'; const result = parse(diff, { diffMaxChanges: 1 }); expect(result).toMatchInlineSnapshot(` Array [ Object { "addedLines": 1, "blocks": Array [ Object { "header": "@@ -101,7 +101,7 @@ var rootjQuery,", "lines": Array [ Object { "content": " // HANDLE: $(function)", "newNumber": 101, "oldNumber": 101, "type": "context", }, Object { "content": " // Shortcut for document ready", "newNumber": 102, "oldNumber": 102, "type": "context", }, Object { "content": " } else if ( jQuery.isFunction( selector ) ) {", "newNumber": 103, "oldNumber": 103, "type": "context", }, Object { "content": "- return typeof rootjQuery.ready !== \\"undefined\\" ?", "newNumber": undefined, "oldNumber": 104, "type": "delete", }, Object { "content": "+ return rootjQuery.ready !== undefined ?", "newNumber": 104, "oldNumber": undefined, "type": "insert", }, ], "newStartLine": 101, "oldStartLine": 101, "oldStartLine2": null, }, ], "checksumAfter": "50f310c", "checksumBefore": "e49196a", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "isTooBig": true, "language": "js", "mode": "100644", "newName": "src/core/init.js", "oldName": "src/core/init.js", }, Object { "addedLines": 0, "blocks": Array [ Object { "header": "@@ -1,6 +1,5 @@", "lines": Array [ Object { "content": " define([", "newNumber": 1, "oldNumber": 1, "type": "context", }, Object { "content": " \\"./core\\",", "newNumber": 2, "oldNumber": 2, "type": "context", }, Object { "content": "- \\"./var/strundefined\\",", "newNumber": undefined, "oldNumber": 3, "type": "delete", }, Object { "content": " \\"./var/rnotwhite\\",", "newNumber": 3, "oldNumber": 4, "type": "context", }, Object { "content": " \\"./var/hasOwn\\",", "newNumber": 4, "oldNumber": 5, "type": "context", }, Object { "content": " \\"./var/slice\\",", "newNumber": 5, "oldNumber": 6, "type": "context", }, ], "newStartLine": 1, "oldStartLine": 1, "oldStartLine2": null, }, ], "checksumAfter": "6183f70", "checksumBefore": "7336f4d", "deletedLines": 1, "isCombined": false, "isGitDiff": true, "language": "js", "mode": "100644", "newName": "src/event.js", "oldName": "src/event.js", }, ] `); }); }); });