The only code that is interested in hover events from gr-comment-diff is in gr-diff-highlight, which is used only by gr-diff. For these reasons, it makes more sense for gr-diff (or gr-diff-highlight) to subscribe to the native hover events of the comment threads associated with it, and not require gr-comment to expose any custom events for that. This also has the benefit that it reduces the requirements on thread elements: They can just fire regular mouseenter and mouseleave events like any DOMElement would. This makes it easier to use gr-diff with different comment thread element implementations. I used this change to also make the unit tests for this more meaningful and not stub out all the interesting parts. Change-Id: I1a969bb6a7092bc433039662cd2034dd7141e4ca
		
			
				
	
	
		
			645 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			645 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"></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-thread-mouseenter from line comments is ignored', () => {
 | 
						|
        const threadEl = document.createElement('div');
 | 
						|
        threadEl.setAttribute('comment-side', 'right');
 | 
						|
        threadEl.setAttribute('line-num', 3);
 | 
						|
        element.appendChild(threadEl);
 | 
						|
        element.commentRanges = [{side: 'right'}];
 | 
						|
 | 
						|
        sandbox.stub(element, 'set');
 | 
						|
        threadEl.dispatchEvent(
 | 
						|
            new CustomEvent('comment-thread-mouseenter', {bubbles: true}));
 | 
						|
        assert.isFalse(element.set.called);
 | 
						|
      });
 | 
						|
 | 
						|
      test('comment-thread-mouseenter from ranged comment causes set', () => {
 | 
						|
        const threadEl = document.createElement('div');
 | 
						|
        threadEl.setAttribute('comment-side', 'right');
 | 
						|
        threadEl.setAttribute('line-num', 3);
 | 
						|
        threadEl.setAttribute('range', JSON.stringify({
 | 
						|
          start_line: 3,
 | 
						|
          start_character: 4,
 | 
						|
          end_line: 5,
 | 
						|
          end_character: 6,
 | 
						|
        }));
 | 
						|
        element.appendChild(threadEl);
 | 
						|
        element.commentRanges = [{side: 'right', range: {
 | 
						|
          start_line: 3,
 | 
						|
          start_character: 4,
 | 
						|
          end_line: 5,
 | 
						|
          end_character: 6,
 | 
						|
        }}];
 | 
						|
 | 
						|
        sandbox.stub(element, 'set');
 | 
						|
        threadEl.dispatchEvent(
 | 
						|
            new CustomEvent('comment-thread-mouseenter', {bubbles: true}));
 | 
						|
        assert.isTrue(element.set.called);
 | 
						|
        const args = element.set.lastCall.args;
 | 
						|
        assert.deepEqual(args[0], ['commentRanges', 0, 'hovering']);
 | 
						|
        assert.deepEqual(args[1], true);
 | 
						|
      });
 | 
						|
 | 
						|
      test('comment-thread-mouseleave from line comments is ignored', () => {
 | 
						|
        const threadEl = document.createElement('div');
 | 
						|
        threadEl.setAttribute('comment-side', 'right');
 | 
						|
        threadEl.setAttribute('line-num', 3);
 | 
						|
        element.appendChild(threadEl);
 | 
						|
        element.commentRanges = [{side: 'right'}];
 | 
						|
 | 
						|
        sandbox.stub(element, 'set');
 | 
						|
        threadEl.dispatchEvent(
 | 
						|
            new CustomEvent('comment-thread-mouseleave', {bubbles: true}));
 | 
						|
        assert.isFalse(element.set.called);
 | 
						|
      });
 | 
						|
 | 
						|
      test('on create-range-comment action box is removed', () => {
 | 
						|
        sandbox.stub(element, '_removeActionBox');
 | 
						|
        element.fire('create-range-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(), {
 | 
						|
          start_line: 138,
 | 
						|
          start_character: 5,
 | 
						|
          end_line: 138,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 119,
 | 
						|
          start_character: 10,
 | 
						|
          end_line: 120,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 119,
 | 
						|
          start_character: 10,
 | 
						|
          end_line: 120,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 119,
 | 
						|
          start_character: 10,
 | 
						|
          end_line: 120,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 140,
 | 
						|
          start_character: 8,
 | 
						|
          end_line: 140,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 140,
 | 
						|
          start_character: 18,
 | 
						|
          end_line: 140,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 140,
 | 
						|
          start_character: 2,
 | 
						|
          end_line: 140,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 140,
 | 
						|
          start_character: 83,
 | 
						|
          end_line: 141,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 140,
 | 
						|
          start_character: 4,
 | 
						|
          end_line: 140,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 130,
 | 
						|
          start_character: 3,
 | 
						|
          end_line: 146,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 140,
 | 
						|
          start_character: 1,
 | 
						|
          end_line: 140,
 | 
						|
          end_character: 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(), {
 | 
						|
          start_line: 140,
 | 
						|
          start_character: 51,
 | 
						|
          end_line: 140,
 | 
						|
          end_character: 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);
 | 
						|
      });
 | 
						|
 | 
						|
      test('_fixTripleClickSelection', () => {
 | 
						|
        const startContent = stubContent(119, 'right');
 | 
						|
        const endContent = stubContent(120, 'right');
 | 
						|
        emulateSelection(startContent.firstChild, 0, endContent.firstChild, 0);
 | 
						|
        assert.isTrue(element.isRangeSelected());
 | 
						|
        assert.deepEqual(getActionRange(), {
 | 
						|
          start_line: 119,
 | 
						|
          start_character: 0,
 | 
						|
          end_line: 119,
 | 
						|
          end_character: element._getLength(startContent),
 | 
						|
        });
 | 
						|
        assert.equal(getActionSide(), 'right');
 | 
						|
      });
 | 
						|
 | 
						|
      test('_fixTripleClickSelection empty line', () => {
 | 
						|
        const startContent = stubContent(146, 'right');
 | 
						|
        const endContent = stubContent(165, 'left');
 | 
						|
        emulateSelection(startContent.firstChild, 0,
 | 
						|
            endContent.parentElement.previousElementSibling, 0);
 | 
						|
        assert.isTrue(element.isRangeSelected());
 | 
						|
        assert.deepEqual(getActionRange(), {
 | 
						|
          start_line: 146,
 | 
						|
          start_character: 0,
 | 
						|
          end_line: 146,
 | 
						|
          end_character: 84,
 | 
						|
        });
 | 
						|
        assert.equal(getActionSide(), 'right');
 | 
						|
      });
 | 
						|
    });
 | 
						|
  });
 | 
						|
</script>
 |