 8cdc76ba4c
			
		
	
	8cdc76ba4c
	
	
	
		
			
			These tags are preserved by the Closure compiler and vulcanize in order to serve the license notices embedded in the outputs. In a standalone Gerrit server, these license are also covered in the LICENSES.txt served with the documentation. When serving PG assets from a CDN, it's less obvious what the corresponding LICENSES.txt file is, since the CDN is not directly linked to a running Gerrit server. Safer to embed the licenses in the assets themselves. Change-Id: Id1add1451fad1baa7916882a6bda02c326ccc988
		
			
				
	
	
		
			631 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			631 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <!DOCTYPE html>
 | |
| <!--
 | |
| @license
 | |
| Copyright (C) 2016 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-highlight</title>
 | |
| 
 | |
| <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 | |
| <script src="../../../bower_components/web-component-tester/browser.js"></script>
 | |
| <link rel="import" href="../../../test/common-test-setup.html"/>
 | |
| <link rel="import" href="gr-diff-highlight.html">
 | |
| 
 | |
| <script>void(0);</script>
 | |
| 
 | |
| <test-fixture id="basic">
 | |
|   <template>
 | |
|     <style>
 | |
|       .tab-indicator:before {
 | |
|         color: #C62828;
 | |
|         /* >> character */
 | |
|         content: '\00BB';
 | |
|       }
 | |
|     </style>
 | |
|     <gr-diff-highlight>
 | |
|       <table id="diffTable">
 | |
| 
 | |
|         <tbody class="section both">
 | |
|            <tr class="diff-row side-by-side" left-type="both" right-type="both">
 | |
|             <td class="left lineNum" data-value="1"></td>
 | |
|             <td class="content both"><div class="contentText">[1] Nam cum ad me in Cumanum salutandi causa uterque venisset,</div></td>
 | |
|             <td class="right lineNum" data-value="1"></td>
 | |
|             <td class="content both"><div class="contentText">[1] Nam cum ad me in Cumanum salutandi causa uterque</div></td>
 | |
|           </tr>
 | |
|         </tbody>
 | |
| 
 | |
|         <tbody class="section delta">
 | |
|           <tr class="diff-row side-by-side" left-type="remove" right-type="add">
 | |
|             <td class="left lineNum" data-value="2"></td>
 | |
|             <!-- Next tag is formatted to eliminate zero-length text nodes. -->
 | |
|             <td class="content remove"><div class="contentText">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab-indicator" style="tab-size:8;">	</span></hl>udiam, <hl>quid</hl> sit, <span class="tab-indicator" style="tab-size:8;">	</span>quod <hl>Epicurum</hl></div></td>
 | |
|             <td class="right lineNum" data-value="2"></td>
 | |
|             <!-- Next tag is formatted to eliminate zero-length text nodes. -->
 | |
|             <td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus <hl><span class="tab-indicator" style="tab-size:8;">	</span></hl> otiosum,  <span class="tab-indicator" style="tab-size:8;">	</span> audiam,  sit, quod</div></td>
 | |
|           </tr>
 | |
|         </tbody>
 | |
| 
 | |
| 
 | |
|         <tbody class="section both">
 | |
|           <tr class="diff-row side-by-side" left-type="both" right-type="both">
 | |
|             <td class="left lineNum" data-value="138"></td>
 | |
|             <td class="content both"><div class="contentText">[14] Nam cum ad me in Cumanum salutandi causa uterque venisset,</div></td>
 | |
|             <td class="right lineNum" data-value="119"></td>
 | |
|             <td class="content both"><div class="contentText">[14] Nam cum ad me in Cumanum salutandi causa uterque venisset,</div></td>
 | |
|           </tr>
 | |
|         </tbody>
 | |
| 
 | |
|         <tbody class="section delta">
 | |
|           <tr class="diff-row side-by-side" left-type="remove" right-type="add">
 | |
|             <td class="left lineNum" data-value="140"></td>
 | |
|             <!-- Next tag is formatted to eliminate zero-length text nodes. -->
 | |
|             <td class="content remove"><div class="contentText">na💢ti <hl class="foo">te, inquit</hl>, sumus <hl class="bar">aliquando</hl> otiosum, <hl>certe</hl> a <hl><span class="tab-indicator" style="tab-size:8;">	</span></hl>udiam, <hl>quid</hl> sit, <span class="tab-indicator" style="tab-size:8;">	</span>quod <hl>Epicurum</hl></div><gr-diff-comment-thread>
 | |
|                 [Yet another random diff thread content here]
 | |
|               </gr-diff-comment-thread></td>
 | |
|             <td class="right lineNum" data-value="120"></td>
 | |
|             <!-- Next tag is formatted to eliminate zero-length text nodes. -->
 | |
|             <td class="content add"><div class="contentText">nacti , <hl>,</hl> sumus <hl><span class="tab-indicator" style="tab-size:8;">	</span></hl> otiosum,  <span class="tab-indicator" style="tab-size:8;">	</span> audiam,  sit, quod</div></td>
 | |
|           </tr>
 | |
|         </tbody>
 | |
| 
 | |
|         <tbody class="section both">
 | |
|           <tr class="diff-row side-by-side" left-type="both" right-type="both">
 | |
|             <td class="left lineNum" data-value="141"></td>
 | |
|             <td class="content both"><div class="contentText">nam et<hl><span class="tab-indicator" style="tab-size:8;">	</span></hl>complectitur<span class="tab-indicator" style="tab-size:8;">	</span>verbis, quod vult, et dicit plane, quod intellegam;</div></td>
 | |
|             <td class="right lineNum" data-value="130"></td>
 | |
|             <td class="content both"><div class="contentText">nam et complectitur verbis, quod vult, et dicit plane, quod intellegam;</div></td>
 | |
|           </tr>
 | |
|         </tbody>
 | |
| 
 | |
|         <tbody class="section contextControl">
 | |
|           <tr class="diff-row side-by-side" left-type="contextControl" right-type="contextControl">
 | |
|             <td class="left contextLineNum" data-value="@@"></td>
 | |
|             <td>
 | |
|               <gr-button>+10↑</gr-button>
 | |
|               -
 | |
|               <gr-button>Show 21 common lines</gr-button>
 | |
|               -
 | |
|               <gr-button>+10↓</gr-button>
 | |
|             </td>
 | |
|             <td class="right contextLineNum" data-value="@@"></td>
 | |
|             <td>
 | |
|               <gr-button>+10↑</gr-button>
 | |
|               -
 | |
|               <gr-button>Show 21 common lines</gr-button>
 | |
|               -
 | |
|               <gr-button>+10↓</gr-button>
 | |
|             </td>
 | |
|           </tr>
 | |
|         </tbody>
 | |
| 
 | |
|         <tbody class="section delta total">
 | |
|           <tr class="diff-row side-by-side" left-type="blank" right-type="add">
 | |
|             <td class="left"></td>
 | |
|             <td class="blank"></td>
 | |
|             <td class="right lineNum" data-value="146"></td>
 | |
|             <td class="content add"><div class="contentText">[17] Quid igitur est? inquit; audire enim cupio, quid non probes. Principio, inquam,</div></td>
 | |
|           </tr>
 | |
|         </tbody>
 | |
| 
 | |
|         <tbody class="section both">
 | |
|           <tr class="diff-row side-by-side" left-type="both" right-type="both">
 | |
|             <td class="left lineNum" data-value="165"></td>
 | |
|             <td class="content both"><div class="contentText">in physicis, quibus maxime gloriatur, primum totus est alienus. Democritea dicit</div></td>
 | |
|             <td class="right lineNum" data-value="147"></td>
 | |
|             <td class="content both"><div class="contentText">in physicis, <hl><span class="tab-indicator" style="tab-size:8;">	</span></hl> quibus maxime gloriatur, primum totus est alienus. Democritea dicit</div></td>
 | |
|           </tr>
 | |
|         </tbody>
 | |
| 
 | |
|       </table>
 | |
|     </gr-diff-highlight>
 | |
|   </template>
 | |
| </test-fixture>
 | |
| 
 | |
| <test-fixture id="highlighted">
 | |
|   <template>
 | |
|     <div>
 | |
|       <hl class="rangeHighlight">foo</hl>
 | |
|       bar
 | |
|       <hl class="rangeHighlight">baz</hl>
 | |
|     </div>
 | |
|   </template>
 | |
| </test-fixture>
 | |
| 
 | |
| <script>
 | |
|   suite('gr-diff-highlight', () => {
 | |
|     let element;
 | |
|     let sandbox;
 | |
| 
 | |
|     setup(() => {
 | |
|       sandbox = sinon.sandbox.create();
 | |
|       element = fixture('basic')[1];
 | |
|     });
 | |
| 
 | |
|     teardown(() => {
 | |
|       sandbox.restore();
 | |
|     });
 | |
| 
 | |
|     suite('selectionchange event handling', () => {
 | |
|       const emulateSelection = function() {
 | |
|         document.dispatchEvent(new CustomEvent('selectionchange'));
 | |
|         element.flushDebouncer('selectionChange');
 | |
|         element.flushDebouncer('removeActionBox');
 | |
|       };
 | |
| 
 | |
|       setup(() => {
 | |
|         sandbox.stub(element, '_handleSelection');
 | |
|         sandbox.stub(element, '_removeActionBox');
 | |
|       });
 | |
| 
 | |
|       test('enabled if logged in', () => {
 | |
|         element.loggedIn = true;
 | |
|         emulateSelection();
 | |
|         assert.isTrue(element._handleSelection.called);
 | |
|         assert.isTrue(element._removeActionBox.called);
 | |
|       });
 | |
| 
 | |
|       test('ignored if logged out', () => {
 | |
|         element.loggedIn = false;
 | |
|         emulateSelection();
 | |
|         assert.isFalse(element._handleSelection.called);
 | |
|         assert.isFalse(element._removeActionBox.called);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     suite('comment events', () => {
 | |
|       let builder;
 | |
| 
 | |
|       setup(() => {
 | |
|         builder = {
 | |
|           getContentsByLineRange: sandbox.stub().returns([]),
 | |
|           getLineElByChild: sandbox.stub().returns({}),
 | |
|           getSideByLineEl: sandbox.stub().returns('other-side'),
 | |
|         };
 | |
|         element._cachedDiffBuilder = builder;
 | |
|       });
 | |
| 
 | |
|       test('comment-mouse-over from line comments is ignored', () => {
 | |
|         sandbox.stub(element, 'set');
 | |
|         element.fire('comment-mouse-over', {comment: {}});
 | |
|         assert.isFalse(element.set.called);
 | |
|       });
 | |
| 
 | |
|       test('comment-mouse-over from ranged comment causes set', () => {
 | |
|         sandbox.stub(element, 'set');
 | |
|         sandbox.stub(element, '_indexOfComment').returns(0);
 | |
|         element.fire('comment-mouse-over', {comment: {range: {}}});
 | |
|         assert.isTrue(element.set.called);
 | |
|       });
 | |
| 
 | |
|       test('comment-mouse-out from line comments is ignored', () => {
 | |
|         element.fire('comment-mouse-over', {comment: {}});
 | |
|         assert.isFalse(builder.getContentsByLineRange.called);
 | |
|       });
 | |
| 
 | |
|       test('on create-comment action box is removed', () => {
 | |
|         sandbox.stub(element, '_removeActionBox');
 | |
|         element.fire('create-comment', {
 | |
|           comment: {
 | |
|             range: {},
 | |
|           },
 | |
|         });
 | |
|         assert.isTrue(element._removeActionBox.called);
 | |
|       });
 | |
|     });
 | |
| 
 | |
|     suite('selection', () => {
 | |
|       let diff;
 | |
|       let builder;
 | |
|       let contentStubs;
 | |
| 
 | |
|       const stubContent = (line, side, opt_child) => {
 | |
|         const contentTd = diff.querySelector(
 | |
|             `.${side}.lineNum[data-value="${line}"] ~ .content`);
 | |
|         const contentText = contentTd.querySelector('.contentText');
 | |
|         const lineEl = diff.querySelector(
 | |
|             `.${side}.lineNum[data-value="${line}"]`);
 | |
|         contentStubs.push({
 | |
|           lineEl,
 | |
|           contentTd,
 | |
|           contentText,
 | |
|         });
 | |
|         builder.getContentByLineEl.withArgs(lineEl).returns(contentText);
 | |
|         builder.getLineNumberByChild.withArgs(lineEl).returns(line);
 | |
|         builder.getContentByLine.withArgs(line, side).returns(contentText);
 | |
|         builder.getSideByLineEl.withArgs(lineEl).returns(side);
 | |
|         return contentText;
 | |
|       };
 | |
| 
 | |
|       const emulateSelection = (startNode, startOffset, endNode, endOffset) => {
 | |
|         const selection = window.getSelection();
 | |
|         const range = document.createRange();
 | |
|         range.setStart(startNode, startOffset);
 | |
|         range.setEnd(endNode, endOffset);
 | |
|         selection.addRange(range);
 | |
|         element._handleSelection();
 | |
|       };
 | |
| 
 | |
|       const getActionRange = () =>
 | |
|           Polymer.dom(element.root).querySelector(
 | |
|               'gr-selection-action-box').range;
 | |
| 
 | |
|       const getActionSide = () =>
 | |
|           Polymer.dom(element.root).querySelector(
 | |
|               'gr-selection-action-box').side;
 | |
| 
 | |
|       const getLineElByChild = node => {
 | |
|         const stubs = contentStubs.find(stub => stub.contentTd.contains(node));
 | |
|         return stubs && stubs.lineEl;
 | |
|       };
 | |
| 
 | |
|       setup(() => {
 | |
|         contentStubs = [];
 | |
|         stub('gr-selection-action-box', {
 | |
|           placeAbove: sandbox.stub(),
 | |
|           placeBelow: sandbox.stub(),
 | |
|         });
 | |
|         diff = element.querySelector('#diffTable');
 | |
|         builder = {
 | |
|           getContentByLine: sandbox.stub(),
 | |
|           getContentByLineEl: sandbox.stub(),
 | |
|           getLineElByChild,
 | |
|           getLineNumberByChild: sandbox.stub(),
 | |
|           getSideByLineEl: sandbox.stub(),
 | |
|         };
 | |
|         element._cachedDiffBuilder = builder;
 | |
|       });
 | |
| 
 | |
|       teardown(() => {
 | |
|         contentStubs = null;
 | |
|         window.getSelection().removeAllRanges();
 | |
|       });
 | |
| 
 | |
|       test('single first line', () => {
 | |
|         const content = stubContent(1, 'right');
 | |
|         sandbox.spy(element, '_positionActionBox');
 | |
|         emulateSelection(content.firstChild, 5, content.firstChild, 12);
 | |
|         const actionBox = element.$$('gr-selection-action-box');
 | |
|         assert.isTrue(actionBox.positionBelow);
 | |
|       });
 | |
| 
 | |
|       test('multiline starting on first line', () => {
 | |
|         const startContent = stubContent(1, 'right');
 | |
|         const endContent = stubContent(2, 'right');
 | |
|         sandbox.spy(element, '_positionActionBox');
 | |
|         emulateSelection(
 | |
|             startContent.firstChild, 10, endContent.lastChild, 7);
 | |
|         const actionBox = element.$$('gr-selection-action-box');
 | |
|         assert.isTrue(actionBox.positionBelow);
 | |
|       });
 | |
| 
 | |
|       test('single line', () => {
 | |
|         const content = stubContent(138, 'left');
 | |
|         sandbox.spy(element, '_positionActionBox');
 | |
|         emulateSelection(content.firstChild, 5, content.firstChild, 12);
 | |
|         const actionBox = element.$$('gr-selection-action-box');
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 138,
 | |
|           startChar: 5,
 | |
|           endLine: 138,
 | |
|           endChar: 12,
 | |
|         });
 | |
|         assert.equal(getActionSide(), 'left');
 | |
|         assert.notOk(actionBox.positionBelow);
 | |
|       });
 | |
| 
 | |
|       test('multiline', () => {
 | |
|         const startContent = stubContent(119, 'right');
 | |
|         const endContent = stubContent(120, 'right');
 | |
|         sandbox.spy(element, '_positionActionBox');
 | |
|         emulateSelection(
 | |
|             startContent.firstChild, 10, endContent.lastChild, 7);
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         const actionBox = element.$$('gr-selection-action-box');
 | |
| 
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 119,
 | |
|           startChar: 10,
 | |
|           endLine: 120,
 | |
|           endChar: 36,
 | |
|         });
 | |
|         assert.equal(getActionSide(), 'right');
 | |
|         assert.notOk(actionBox.positionBelow);
 | |
|       });
 | |
| 
 | |
|       test('multiple ranges aka firefox implementation', () => {
 | |
|         const startContent = stubContent(119, 'right');
 | |
|         const endContent = stubContent(120, 'right');
 | |
| 
 | |
|         const startRange = document.createRange();
 | |
|         startRange.setStart(startContent.firstChild, 10);
 | |
|         startRange.setEnd(startContent.firstChild, 11);
 | |
| 
 | |
|         const endRange = document.createRange();
 | |
|         endRange.setStart(endContent.lastChild, 6);
 | |
|         endRange.setEnd(endContent.lastChild, 7);
 | |
| 
 | |
|         const getRangeAtStub = sandbox.stub();
 | |
|         getRangeAtStub
 | |
|             .onFirstCall().returns(startRange)
 | |
|             .onSecondCall().returns(endRange);
 | |
|         sandbox.stub(window, 'getSelection').returns({
 | |
|           rangeCount: 2,
 | |
|           getRangeAt: getRangeAtStub,
 | |
|           removeAllRanges: sandbox.stub(),
 | |
|         });
 | |
|         element._handleSelection();
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 119,
 | |
|           startChar: 10,
 | |
|           endLine: 120,
 | |
|           endChar: 36,
 | |
|         });
 | |
|       });
 | |
| 
 | |
|       test('multiline grow end highlight over tabs', () => {
 | |
|         const startContent = stubContent(119, 'right');
 | |
|         const endContent = stubContent(120, 'right');
 | |
|         emulateSelection(startContent.firstChild, 10, endContent.firstChild, 2);
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 119,
 | |
|           startChar: 10,
 | |
|           endLine: 120,
 | |
|           endChar: 2,
 | |
|         });
 | |
|         assert.equal(getActionSide(), 'right');
 | |
|       });
 | |
| 
 | |
|       test('collapsed', () => {
 | |
|         const content = stubContent(138, 'left');
 | |
|         emulateSelection(content.firstChild, 5, content.firstChild, 5);
 | |
|         assert.isOk(window.getSelection().getRangeAt(0).startContainer);
 | |
|         assert.isFalse(element.isRangeSelected());
 | |
|       });
 | |
| 
 | |
|       test('starts inside hl', () => {
 | |
|         const content = stubContent(140, 'left');
 | |
|         const hl = content.querySelector('.foo');
 | |
|         emulateSelection(hl.firstChild, 2, hl.nextSibling, 7);
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 140,
 | |
|           startChar: 8,
 | |
|           endLine: 140,
 | |
|           endChar: 23,
 | |
|         });
 | |
|         assert.equal(getActionSide(), 'left');
 | |
|       });
 | |
| 
 | |
|       test('ends inside hl', () => {
 | |
|         const content = stubContent(140, 'left');
 | |
|         const hl = content.querySelector('.bar');
 | |
|         emulateSelection(hl.previousSibling, 2, hl.firstChild, 3);
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 140,
 | |
|           startChar: 18,
 | |
|           endLine: 140,
 | |
|           endChar: 27,
 | |
|         });
 | |
|       });
 | |
| 
 | |
|       test('multiple hl', () => {
 | |
|         const content = stubContent(140, 'left');
 | |
|         const hl = content.querySelectorAll('hl')[4];
 | |
|         emulateSelection(content.firstChild, 2, hl.firstChild, 2);
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 140,
 | |
|           startChar: 2,
 | |
|           endLine: 140,
 | |
|           endChar: 61,
 | |
|         });
 | |
|         assert.equal(getActionSide(), 'left');
 | |
|       });
 | |
| 
 | |
|       test('starts outside of diff', () => {
 | |
|         const contentText = stubContent(140, 'left');
 | |
|         const contentTd = contentText.parentElement;
 | |
| 
 | |
|         emulateSelection(contentTd.previousElementSibling, 0,
 | |
|             contentText.firstChild, 2);
 | |
|         assert.isFalse(element.isRangeSelected());
 | |
|       });
 | |
| 
 | |
|       test('ends outside of diff', () => {
 | |
|         const content = stubContent(140, 'left');
 | |
|         emulateSelection(content.nextElementSibling.firstChild, 2,
 | |
|             content.firstChild, 2);
 | |
|         assert.isFalse(element.isRangeSelected());
 | |
|       });
 | |
| 
 | |
|       test('starts and ends on different sides', () => {
 | |
|         const startContent = stubContent(140, 'left');
 | |
|         const endContent = stubContent(130, 'right');
 | |
|         emulateSelection(startContent.firstChild, 2, endContent.firstChild, 2);
 | |
|         assert.isFalse(element.isRangeSelected());
 | |
|       });
 | |
| 
 | |
|       test('starts in comment thread element', () => {
 | |
|         const startContent = stubContent(140, 'left');
 | |
|         const comment = startContent.parentElement.querySelector(
 | |
|             'gr-diff-comment-thread');
 | |
|         const endContent = stubContent(141, 'left');
 | |
|         emulateSelection(comment.firstChild, 2, endContent.firstChild, 4);
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 140,
 | |
|           startChar: 83,
 | |
|           endLine: 141,
 | |
|           endChar: 4,
 | |
|         });
 | |
|         assert.equal(getActionSide(), 'left');
 | |
|       });
 | |
| 
 | |
|       test('ends in comment thread element', () => {
 | |
|         const content = stubContent(140, 'left');
 | |
|         const comment = content.parentElement.querySelector(
 | |
|             'gr-diff-comment-thread');
 | |
|         emulateSelection(content.firstChild, 4, comment.firstChild, 1);
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 140,
 | |
|           startChar: 4,
 | |
|           endLine: 140,
 | |
|           endChar: 83,
 | |
|         });
 | |
|         assert.equal(getActionSide(), 'left');
 | |
|       });
 | |
| 
 | |
|       test('starts in context element', () => {
 | |
|         const contextControl =
 | |
|             diff.querySelector('.contextControl').querySelector('gr-button');
 | |
|         const content = stubContent(146, 'right');
 | |
|         emulateSelection(contextControl, 0, content.firstChild, 7);
 | |
|         // TODO (viktard): Select nearest line.
 | |
|         assert.isFalse(element.isRangeSelected());
 | |
|       });
 | |
| 
 | |
|       test('ends in context element', () => {
 | |
|         const contextControl =
 | |
|             diff.querySelector('.contextControl').querySelector('gr-button');
 | |
|         const content = stubContent(141, 'left');
 | |
|         emulateSelection(content.firstChild, 2, contextControl, 1);
 | |
|         // TODO (viktard): Select nearest line.
 | |
|         assert.isFalse(element.isRangeSelected());
 | |
|       });
 | |
| 
 | |
|       test('selection containing context element', () => {
 | |
|         const startContent = stubContent(130, 'right');
 | |
|         const endContent = stubContent(146, 'right');
 | |
|         emulateSelection(startContent.firstChild, 3, endContent.firstChild, 14);
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 130,
 | |
|           startChar: 3,
 | |
|           endLine: 146,
 | |
|           endChar: 14,
 | |
|         });
 | |
|         assert.equal(getActionSide(), 'right');
 | |
|       });
 | |
| 
 | |
|       test('ends at a tab', () => {
 | |
|         const content = stubContent(140, 'left');
 | |
|         emulateSelection(
 | |
|             content.firstChild, 1, content.querySelector('span'), 0);
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 140,
 | |
|           startChar: 1,
 | |
|           endLine: 140,
 | |
|           endChar: 51,
 | |
|         });
 | |
|         assert.equal(getActionSide(), 'left');
 | |
|       });
 | |
| 
 | |
|       test('starts at a tab', () => {
 | |
|         const content = stubContent(140, 'left');
 | |
|         emulateSelection(
 | |
|             content.querySelectorAll('hl')[3], 0,
 | |
|             content.querySelectorAll('span')[1].nextSibling, 1);
 | |
|         assert.isTrue(element.isRangeSelected());
 | |
|         assert.deepEqual(getActionRange(), {
 | |
|           startLine: 140,
 | |
|           startChar: 51,
 | |
|           endLine: 140,
 | |
|           endChar: 71,
 | |
|         });
 | |
|         assert.equal(getActionSide(), 'left');
 | |
|       });
 | |
| 
 | |
|       test('properly accounts for syntax highlighting', () => {
 | |
|         const content = stubContent(140, 'left');
 | |
|         const spy = sinon.spy(element, '_normalizeRange');
 | |
|         emulateSelection(
 | |
|             content.querySelectorAll('hl')[3], 0,
 | |
|             content.querySelectorAll('span')[1], 0);
 | |
|         const spyCall = spy.getCall(0);
 | |
|         const range = window.getSelection().getRangeAt(0);
 | |
|         assert.notDeepEqual(spyCall.returnValue, range);
 | |
|       });
 | |
| 
 | |
|       test('GrRangeNormalizer._getTextOffset computes text offset', () => {
 | |
|         let content = stubContent(140, 'left');
 | |
|         let child = content.lastChild.lastChild;
 | |
|         let result = GrRangeNormalizer._getTextOffset(content, child);
 | |
|         assert.equal(result, 75);
 | |
|         content = stubContent(146, 'right');
 | |
|         child = content.lastChild;
 | |
|         result = GrRangeNormalizer._getTextOffset(content, child);
 | |
|         assert.equal(result, 0);
 | |
|       });
 | |
| 
 | |
|       // TODO (viktard): Selection starts in line number.
 | |
|       // TODO (viktard): Empty lines in selection start.
 | |
|       // TODO (viktard): Empty lines in selection end.
 | |
|       // TODO (viktard): Only empty lines selected.
 | |
|       // TODO (viktard): Unified mode.
 | |
| 
 | |
|       suite('triple click', () => {
 | |
|         test('_fixTripleClickSelection', () => {
 | |
|           const fakeRange = {
 | |
|             startContainer: '',
 | |
|             startOffset: '',
 | |
|             endContainer: '',
 | |
|             endOffset: '',
 | |
|           };
 | |
|           const fixedRange = {};
 | |
|           sandbox.stub(GrRangeNormalizer, 'normalize').returns(fakeRange);
 | |
|           sandbox.stub(element, '_normalizeSelectionSide');
 | |
|           sandbox.stub(element, '_fixTripleClickSelection').returns(fixedRange);
 | |
|           assert.strictEqual(element._normalizeRange({}), fixedRange);
 | |
|           assert.isTrue(element._fixTripleClickSelection.called);
 | |
|         });
 | |
| 
 | |
|         test('left pane', () => {
 | |
|           const startNode = stubContent(138, 'left');
 | |
|           const endNode =
 | |
|               stubContent(119, 'right').parentElement.previousElementSibling;
 | |
|           builder.getLineNumberByChild.withArgs(endNode).returns(119);
 | |
|           emulateSelection(startNode, 0, endNode, 0);
 | |
|           assert.deepEqual(getActionRange(), {
 | |
|             startLine: 138,
 | |
|             startChar: 0,
 | |
|             endLine: 138,
 | |
|             endChar: 63,
 | |
|           });
 | |
|         });
 | |
| 
 | |
|         test('right pane', () => {
 | |
|           const startNode = stubContent(119, 'right');
 | |
|           const endNode =
 | |
|               stubContent(140, 'left').parentElement.previousElementSibling;
 | |
|           emulateSelection(startNode, 0, endNode, 0);
 | |
|           assert.deepEqual(getActionRange(), {
 | |
|             startLine: 119,
 | |
|             startChar: 0,
 | |
|             endLine: 119,
 | |
|             endChar: 63,
 | |
|           });
 | |
|         });
 | |
|       });
 | |
|     });
 | |
|   });
 | |
| </script>
 |