| <!DOCTYPE html> |
| <!-- |
| Copyright (C) 2015 The Android Open Source Project |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> |
| <title>gr-diff-view</title> |
| |
| <script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script> |
| <script src="../../bower_components/web-component-tester/browser.js"></script> |
| <script src="../../bower_components/page/page.js"></script> |
| <script src="../scripts/changes.js"></script> |
| <script src="../scripts/util.js"></script> |
| |
| <link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html"> |
| <link rel="import" href="../elements/gr-diff-view.html"> |
| |
| <test-fixture id="basic"> |
| <template> |
| <gr-diff-view ruler-width="0"></gr-diff-view> |
| </template> |
| </test-fixture> |
| |
| <test-fixture id="comments"> |
| <template> |
| <gr-diff-view></gr-diff-view> |
| </template> |
| </test-fixture> |
| |
| <test-fixture id="manylines"> |
| <template> |
| <gr-diff-view></gr-diff-view> |
| </template> |
| </test-fixture> |
| |
| |
| <script> |
| // Original diff: |
| // Left side (side A): |
| // 1: if i < 5 { // "comments" &= \'fun\' && true |
| // 2: println("i is less than five") |
| // 3: } |
| // 4: |
| // 5: |
| // 6: // Comment |
| // 7: foo |
| // 8: bar |
| // 9: Bad news: combustible lemons failed. |
| // |
| // Right side (side B): |
| // 1: if i < 5 { // "comments" &= \'fun\' && true |
| // 2: println("i is less than five") |
| // 3: } |
| // 4: |
| // 5: |
| // 6: // Comment |
| // 7: baz |
| // 8: Bad news: combustible lemons failed. |
| // |
| var diffContent = [ |
| { |
| ab: [ |
| 'if i < 5 { // "comments" &= \'fun\' && true', |
| ' println("i is less than five")', |
| '}', |
| '', |
| '', |
| '// Comment' |
| ] |
| }, |
| { |
| a: [ |
| 'foo', |
| 'bar' |
| ], |
| b: [ |
| 'baz', |
| ] |
| }, |
| { |
| ab: [ |
| 'Bad news: combustible lemons failed.' |
| ] |
| } |
| ]; |
| |
| suite('<gr-diff-view>', function() { |
| var element; |
| |
| setup(function() { |
| element = fixture('basic'); |
| element._maybeRenderDiff({content: diffContent}, [], []); |
| }); |
| |
| test('ab content is the same for left and right sides', function() { |
| for (var i = 0; i < diffContent[0].ab.length; i++) { |
| var leftEls = Polymer.dom(element.root).querySelectorAll( |
| '#leftDiffContent .content[data-line-num="' + (i + 1) + '"]'); |
| assert.equal(leftEls.length, 1); |
| var rightEls = Polymer.dom(element.root).querySelectorAll( |
| '#rightDiffContent .content[data-line-num="' + (i + 1) + '"]'); |
| assert.equal(rightEls.length, 1); |
| assert.equal(leftEls[0].textContent, rightEls[0].textContent); |
| } |
| }); |
| |
| test('all line number and content elements have same (non-zero) height', |
| function() { |
| var els = Polymer.dom(element.root).querySelectorAll('.lineNum, .content'); |
| assert.isAbove(els.length, 0); |
| var offsetHeight = els.length > 0 && els[0].offsetHeight; |
| assert.isAbove(offsetHeight, 0); |
| for (var i = 0; i < els.length; i++) { |
| assert.equal(offsetHeight, els[i].offsetHeight); |
| } |
| }); |
| |
| test('content is properly escaped', function() { |
| var firstLineEls = Polymer.dom(element.root).querySelectorAll( |
| '#leftDiffContent .content[data-line-num="1"], ' + |
| '#rightDiffContent .content[data-line-num="1"]'); |
| assert.equal(2, firstLineEls.length); |
| for (var i = 0; i < firstLineEls.length; i++) { |
| assert.equal(firstLineEls[i].innerHTML, |
| 'if i < 5 { // "comments" &= \'fun\' && true'); |
| } |
| }); |
| |
| test('content and line numbers are correct for a/b edit', function() { |
| assert.equal(element.$$( |
| '#leftDiffContent .content[data-line-num="7"]').textContent, 'foo'); |
| assert.equal(element.$$( |
| '#leftDiffContent .content[data-line-num="8"]').textContent, 'bar'); |
| assert.equal(element.$$( |
| '#rightDiffContent .content[data-line-num="7"]').textContent, 'baz'); |
| assert.equal(element.$$( |
| '#leftDiffContent .content[data-line-num="9"]').textContent, |
| element.$$( |
| '#rightDiffContent .content[data-line-num="8"]').textContent); |
| }); |
| |
| test('ruler width changes are applied correctly', function() { |
| assert.equal(element.rulerWidth, 0); |
| assert.equal(Polymer.dom(element.root).querySelectorAll('.ruler').length, |
| 0); |
| element.rulerWidth = 80; |
| flushAsynchronousOperations(); |
| var els = Polymer.dom(element.root).querySelectorAll('.ruler'); |
| assert.isAbove(els.length, 0); |
| for (var i = 0; i < els.length; i++) { |
| assert.equal(els[i].style.left, '80ch'); |
| } |
| element.rulerWidth = 0; |
| flushAsynchronousOperations(); |
| assert.equal(Polymer.dom(element.root).querySelectorAll('.ruler').length, |
| 0); |
| element.rulerWidth = 100; |
| flushAsynchronousOperations(); |
| els = Polymer.dom(element.root).querySelectorAll('.ruler'); |
| assert.isAbove(els.length, 0); |
| for (var i = 0; i < els.length; i++) { |
| assert.equal(els[i].style.left, '100ch'); |
| } |
| }); |
| |
| }); |
| |
| suite('comments', function() { |
| var element; |
| |
| setup(function() { |
| element = fixture('comments'); |
| element._patchNum = 1; |
| element._maybeRenderDiff({content: diffContent}, [], [ |
| { |
| id: 'file_comment', |
| message: 'this is a file comment about the meaninglessness of life', |
| author: { |
| name: 'GLaDOS' |
| } |
| }, |
| { |
| id: 'all_the_lemons', |
| line: 8, |
| message: 'MAKE LIFE TAKE THE LEMONS BACK', |
| author: { |
| name: 'Cave Johnson', |
| }, |
| } |
| ]); |
| }); |
| |
| test('comment threads are rendered correctly', function(done) { |
| // On WebKit and Gecko, flushAsynchronousOperations isn't enough to allow |
| // the thread filler elements to properly render. Wait for the resize |
| // events that trigger their addition and check after the expected number |
| // come in. |
| var numEventsFired = 0; |
| element.addEventListener('gr-diff-comment-thread-height-changed', |
| function() { |
| numEventsFired++; |
| if (numEventsFired < 2) { return; } |
| assert.equal(numEventsFired, 2); |
| |
| var threadEls = Polymer.dom(element.root).querySelectorAll( |
| 'gr-diff-comment-thread[data-thread-id="thread-1-8"]'); |
| assert.equal(threadEls.length, 1); |
| var fillerEls = Polymer.dom(element.root).querySelectorAll( |
| '.js-threadFiller[data-thread-id="thread-1-8"]'); |
| assert.equal(fillerEls.length, 3); |
| |
| threadEls = Polymer.dom(element.root).querySelectorAll( |
| 'gr-diff-comment-thread[data-thread-id="thread-1-FILE"]'); |
| assert.equal(threadEls.length, 1); |
| fillerEls = Polymer.dom(element.root).querySelectorAll( |
| '.js-threadFiller[data-thread-id="thread-1-FILE"]'); |
| assert.equal(fillerEls.length, 3); |
| |
| done(); |
| }); |
| }); |
| }); |
| |
| suite('long diffs', function() { |
| var element; |
| |
| setup(function() { |
| element = fixture('manylines'); |
| var longDiffContent = [{ ab: [] }]; |
| for (var i = 0; i < 300; i++) { |
| longDiffContent[0].ab.push(''); |
| } |
| element._maybeRenderDiff({content: longDiffContent}, [], []); |
| }); |
| |
| function isVisibleInWindow(el) { |
| var rect = el.getBoundingClientRect(); |
| return rect.top >= 0 && rect.left >= 0 && |
| rect.bottom <= window.innerHeight && rect.right <= window.innerWidth; |
| } |
| |
| test('jump to line', function() { |
| element._jumpToLine(-12849); |
| assert.equal(window.scrollY, 0); |
| element._jumpToLine('sup'); |
| assert.equal(window.scrollY, 0); |
| // Use the left line numbers in this case because the viewport may be too |
| // thin for the right line number element to be visible. Since the content |
| // is the same for both sides, it should not make a difference. |
| var lineEl = |
| element.$$('.diffNumbers.left .lineNum[data-line-num="150"]'); |
| assert.isFalse(isVisibleInWindow(lineEl), |
| 'element should not be visible'); |
| element._jumpToLine(150); |
| assert.isAbove(window.scrollY, 0); |
| assert.isTrue(isVisibleInWindow(lineEl), 'element should be visible'); |
| }); |
| |
| test('keyboard shortcuts', function() { |
| element._changeNum = '42'; |
| element._patchNum = '10'; |
| element._fileList = ['chell.go', 'glados.txt', 'wheatley.md']; |
| element._path = 'glados.txt'; |
| |
| var showStub = sinon.stub(page, 'show'); |
| MockInteractions.pressAndReleaseKeyOn(element, 85); // 'u' |
| assert(showStub.lastCall.calledWithExactly('/c/42'), |
| 'Should navigate to /c/42'); |
| |
| pressAndReleaseKeyIdentifierOn(element, '\U+005D'); // ']' |
| assert(showStub.lastCall.calledWithExactly('/c/42/10/wheatley.md'), |
| 'Should navigate to /c/42/10/wheatley.md'); |
| element._path = 'wheatley.md'; |
| |
| pressAndReleaseKeyIdentifierOn(element, '\U+005B'); // '[' |
| assert(showStub.lastCall.calledWithExactly('/c/42/10/glados.txt'), |
| 'Should navigate to /c/42/10/glados.txt'); |
| element._path = 'glados.txt'; |
| |
| pressAndReleaseKeyIdentifierOn(element, '\U+005B'); // '[' |
| assert(showStub.lastCall.calledWithExactly('/c/42/10/chell.go'), |
| 'Should navigate to /c/42/10/chell.go'); |
| element._path = 'chell.go'; |
| |
| pressAndReleaseKeyIdentifierOn(element, '\U+005B'); // '[' |
| assert(showStub.lastCall.calledWithExactly('/c/42'), |
| 'Should navigate to /c/42'); |
| |
| showStub.restore(); |
| |
| // https://github.com/PolymerElements/iron-test-helpers/issues/33 |
| function keyboardEventFor(type, keyIdentifier) { |
| var event = new CustomEvent(type, { |
| bubbles: true, |
| cancelable: true |
| }); |
| |
| event.keyIdentifier = keyIdentifier; |
| |
| return event; |
| } |
| |
| function keyEventOn(target, type, keyIdentifier) { |
| target.dispatchEvent(keyboardEventFor(type, keyIdentifier)); |
| } |
| |
| function keyDownOn(target, keyIdentifier) { |
| keyEventOn(target, 'keydown', keyIdentifier); |
| } |
| |
| function keyUpOn(target, keyIdentifier) { |
| keyEventOn(target, 'keyup', keyIdentifier); |
| } |
| |
| function pressAndReleaseKeyIdentifierOn(target, keyIdentifier) { |
| keyDownOn(target, keyIdentifier); |
| Polymer.Base.async(function() { |
| keyUpOn(target, keyIdentifier); |
| }, 1); |
| } |
| }); |
| }); |
| </script> |