This change decouples gr-diff-builder and gr-syntax-layer. gr-syntax- layer will now live in gr-diff-host. As a result, from the perspective of gr-diff/gr-diff-builder, plugin layers and the syntax layer will now be part of the same list. A future potential refactor could also lift gr-coverage-layer instances in a similar fashion. Change-Id: Ic10b45d70a59c2e18d3b408880f66251ab93e06e
		
			
				
	
	
		
			1152 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			1152 lines
		
	
	
		
			40 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-builder</title>
 | 
						|
<script src="/test/common-test-setup.js"></script>
 | 
						|
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
 | 
						|
 | 
						|
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
 | 
						|
<script src="/bower_components/web-component-tester/browser.js"></script>
 | 
						|
<link rel="import" href="../../../test/common-test-setup.html"/>
 | 
						|
<script src="../../../scripts/util.js"></script>
 | 
						|
<script src="../gr-diff/gr-diff-line.js"></script>
 | 
						|
<script src="../gr-diff/gr-diff-group.js"></script>
 | 
						|
<script src="../gr-diff-highlight/gr-annotation.js"></script>
 | 
						|
<script src="gr-diff-builder.js"></script>
 | 
						|
 | 
						|
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
 | 
						|
<link rel="import" href="gr-diff-builder.html">
 | 
						|
 | 
						|
<script>void(0);</script>
 | 
						|
 | 
						|
<test-fixture id="basic">
 | 
						|
  <template is="dom-template">
 | 
						|
    <gr-diff-builder>
 | 
						|
      <table id="diffTable"></table>
 | 
						|
    </gr-diff-builder>
 | 
						|
  </template>
 | 
						|
</test-fixture>
 | 
						|
 | 
						|
<test-fixture id="div-with-text">
 | 
						|
  <template>
 | 
						|
    <div>Lorem ipsum dolor sit amet, suspendisse inceptos vehicula</div>
 | 
						|
  </template>
 | 
						|
</test-fixture>
 | 
						|
 | 
						|
<test-fixture id="mock-diff">
 | 
						|
  <template>
 | 
						|
    <gr-diff-builder view-mode="SIDE_BY_SIDE">
 | 
						|
      <table id="diffTable"></table>
 | 
						|
    </gr-diff-builder>
 | 
						|
  </template>
 | 
						|
</test-fixture>
 | 
						|
 | 
						|
<script>
 | 
						|
  suite('gr-diff-builder tests', () => {
 | 
						|
    let prefs;
 | 
						|
    let element;
 | 
						|
    let builder;
 | 
						|
    let sandbox;
 | 
						|
    const LINE_FEED_HTML = '<span class="style-scope gr-diff br"></span>';
 | 
						|
 | 
						|
    setup(() => {
 | 
						|
      sandbox = sinon.sandbox.create();
 | 
						|
      element = fixture('basic');
 | 
						|
      stub('gr-rest-api-interface', {
 | 
						|
        getLoggedIn() { return Promise.resolve(false); },
 | 
						|
        getProjectConfig() { return Promise.resolve({}); },
 | 
						|
      });
 | 
						|
      prefs = {
 | 
						|
        line_length: 10,
 | 
						|
        show_tabs: true,
 | 
						|
        tab_size: 4,
 | 
						|
      };
 | 
						|
      builder = new GrDiffBuilder({content: []}, prefs);
 | 
						|
    });
 | 
						|
 | 
						|
    teardown(() => { sandbox.restore(); });
 | 
						|
 | 
						|
    test('_createElement classStr applies all classes', () => {
 | 
						|
      const node = builder._createElement('div', 'test classes');
 | 
						|
      assert.isTrue(node.classList.contains('gr-diff'));
 | 
						|
      assert.isTrue(node.classList.contains('test'));
 | 
						|
      assert.isTrue(node.classList.contains('classes'));
 | 
						|
    });
 | 
						|
 | 
						|
    test('context control buttons', () => {
 | 
						|
      // Create 10 lines.
 | 
						|
      const lines = [];
 | 
						|
      for (let i = 0; i < 10; i++) {
 | 
						|
        const line = new GrDiffLine(GrDiffLine.Type.BOTH);
 | 
						|
        line.beforeNumber = i + 1;
 | 
						|
        line.afterNumber = i + 1;
 | 
						|
        line.text = 'lorem upsum';
 | 
						|
        lines.push(line);
 | 
						|
      }
 | 
						|
 | 
						|
      const contextLine = {
 | 
						|
        contextGroups: [new GrDiffGroup(GrDiffGroup.Type.BOTH, lines)],
 | 
						|
      };
 | 
						|
 | 
						|
      const section = {};
 | 
						|
      // Does not include +10 buttons when there are fewer than 11 lines.
 | 
						|
      let td = builder._createContextControl(section, contextLine);
 | 
						|
      let buttons = td.querySelectorAll('gr-button.showContext');
 | 
						|
 | 
						|
      assert.equal(buttons.length, 1);
 | 
						|
      assert.equal(Polymer.dom(buttons[0]).textContent, 'Show 10 common lines');
 | 
						|
 | 
						|
      // Add another line.
 | 
						|
      const line = new GrDiffLine(GrDiffLine.Type.BOTH);
 | 
						|
      line.text = 'lorem upsum';
 | 
						|
      line.beforeNumber = 11;
 | 
						|
      line.afterNumber = 11;
 | 
						|
      contextLine.contextGroups[0].addLine(line);
 | 
						|
 | 
						|
      // Includes +10 buttons when there are at least 11 lines.
 | 
						|
      td = builder._createContextControl(section, contextLine);
 | 
						|
      buttons = td.querySelectorAll('gr-button.showContext');
 | 
						|
 | 
						|
      assert.equal(buttons.length, 3);
 | 
						|
      assert.equal(Polymer.dom(buttons[0]).textContent, '+10↑');
 | 
						|
      assert.equal(Polymer.dom(buttons[1]).textContent, 'Show 11 common lines');
 | 
						|
      assert.equal(Polymer.dom(buttons[2]).textContent, '+10↓');
 | 
						|
    });
 | 
						|
 | 
						|
    test('newlines 1', () => {
 | 
						|
      let text = 'abcdef';
 | 
						|
 | 
						|
      assert.equal(builder._formatText(text, 4, 10).innerHTML, text);
 | 
						|
      text = 'a'.repeat(20);
 | 
						|
      assert.equal(builder._formatText(text, 4, 10).innerHTML,
 | 
						|
          'a'.repeat(10) +
 | 
						|
          LINE_FEED_HTML +
 | 
						|
          'a'.repeat(10));
 | 
						|
    });
 | 
						|
 | 
						|
    test('newlines 2', () => {
 | 
						|
      const text = '<span class="thumbsup">👍</span>';
 | 
						|
      assert.equal(builder._formatText(text, 4, 10).innerHTML,
 | 
						|
          '<span clas' +
 | 
						|
          LINE_FEED_HTML +
 | 
						|
          's="thumbsu' +
 | 
						|
          LINE_FEED_HTML +
 | 
						|
          'p">👍</span' +
 | 
						|
          LINE_FEED_HTML +
 | 
						|
          '>');
 | 
						|
    });
 | 
						|
 | 
						|
    test('newlines 3', () => {
 | 
						|
      const text = '01234\t56789';
 | 
						|
      assert.equal(builder._formatText(text, 4, 10).innerHTML,
 | 
						|
          '01234' + builder._getTabWrapper(3).outerHTML + '56' +
 | 
						|
          LINE_FEED_HTML +
 | 
						|
          '789');
 | 
						|
    });
 | 
						|
 | 
						|
    test('newlines 4', () => {
 | 
						|
      const text = '👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍';
 | 
						|
      assert.equal(builder._formatText(text, 4, 20).innerHTML,
 | 
						|
          '👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍' +
 | 
						|
          LINE_FEED_HTML +
 | 
						|
          '👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍' +
 | 
						|
          LINE_FEED_HTML +
 | 
						|
          '👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍');
 | 
						|
    });
 | 
						|
 | 
						|
 | 
						|
    test('line_length ignored if line_wrapping is true', () => {
 | 
						|
      builder._prefs = {line_wrapping: true, tab_size: 4, line_length: 50};
 | 
						|
      const text = 'a'.repeat(51);
 | 
						|
 | 
						|
      const line = {text, highlights: []};
 | 
						|
      const result = builder._createTextEl(undefined, line).firstChild.innerHTML;
 | 
						|
      assert.equal(result, text);
 | 
						|
    });
 | 
						|
 | 
						|
    test('line_length applied if line_wrapping is false', () => {
 | 
						|
      builder._prefs = {line_wrapping: false, tab_size: 4, line_length: 50};
 | 
						|
      const text = 'a'.repeat(51);
 | 
						|
 | 
						|
      const line = {text, highlights: []};
 | 
						|
      const expected = 'a'.repeat(50) + LINE_FEED_HTML + 'a';
 | 
						|
      const result = builder._createTextEl(undefined, line).firstChild.innerHTML;
 | 
						|
      assert.equal(result, expected);
 | 
						|
    });
 | 
						|
 | 
						|
    test('_createTextEl linewrap with tabs', () => {
 | 
						|
      const text = '\t'.repeat(7) + '!';
 | 
						|
      const line = {text, highlights: []};
 | 
						|
      const el = builder._createTextEl(undefined, line);
 | 
						|
      assert.equal(el.innerText, text);
 | 
						|
      // With line length 10 and tab size 2, there should be a line break
 | 
						|
      // after every two tabs.
 | 
						|
      const newlineEl = el.querySelector('.contentText > .br');
 | 
						|
      assert.isOk(newlineEl);
 | 
						|
      assert.equal(
 | 
						|
          el.querySelector('.contentText .tab:nth-child(2)').nextSibling,
 | 
						|
          newlineEl);
 | 
						|
    });
 | 
						|
 | 
						|
    test('text length with tabs and unicode', () => {
 | 
						|
      function expectTextLength(text, tabSize, expected) {
 | 
						|
        // Formatting to |expected| columns should not introduce line breaks.
 | 
						|
        const result = builder._formatText(text, tabSize, expected);
 | 
						|
        assert.isNotOk(result.querySelector('.contentText > .br'),
 | 
						|
            `  Expected the result of: \n` +
 | 
						|
            `      _formatText(${text}', ${tabSize}, ${expected})\n` +
 | 
						|
            `  to not contain a br. But the actual result HTML was:\n` +
 | 
						|
            `      '${result.innerHTML}'\nwhereupon`);
 | 
						|
 | 
						|
        // Increasing the line limit should produce the same markup.
 | 
						|
        assert.equal(builder._formatText(text, tabSize, Infinity).innerHTML,
 | 
						|
            result.innerHTML);
 | 
						|
        assert.equal(builder._formatText(text, tabSize, expected + 1).innerHTML,
 | 
						|
            result.innerHTML);
 | 
						|
 | 
						|
        // Decreasing the line limit should introduce line breaks.
 | 
						|
        if (expected > 0) {
 | 
						|
          const tooSmall = builder._formatText(text, tabSize, expected - 1);
 | 
						|
          assert.isOk(tooSmall.querySelector('.contentText > .br'),
 | 
						|
              `  Expected the result of: \n` +
 | 
						|
              `      _formatText(${text}', ${tabSize}, ${expected - 1})\n` +
 | 
						|
              `  to contain a br. But the actual result HTML was:\n` +
 | 
						|
              `      '${tooSmall.innerHTML}'\nwhereupon`);
 | 
						|
        }
 | 
						|
      }
 | 
						|
      expectTextLength('12345', 4, 5);
 | 
						|
      expectTextLength('\t\t12', 4, 10);
 | 
						|
      expectTextLength('abc💢123', 4, 7);
 | 
						|
      expectTextLength('abc\t', 8, 8);
 | 
						|
      expectTextLength('abc\t\t', 10, 20);
 | 
						|
      expectTextLength('', 10, 0);
 | 
						|
      expectTextLength('', 10, 0);
 | 
						|
      // 17 Thai combining chars.
 | 
						|
      expectTextLength('ก้้้้้้้้้้้้้้้้', 4, 17);
 | 
						|
      expectTextLength('abc\tde', 10, 12);
 | 
						|
      expectTextLength('abc\tde\t', 10, 20);
 | 
						|
      expectTextLength('\t\t\t\t\t', 20, 100);
 | 
						|
    });
 | 
						|
 | 
						|
    test('tab wrapper insertion', () => {
 | 
						|
      const html = 'abc\tdef';
 | 
						|
      const tabSize = builder._prefs.tab_size;
 | 
						|
      const wrapper = builder._getTabWrapper(tabSize - 3);
 | 
						|
      assert.ok(wrapper);
 | 
						|
      assert.equal(wrapper.innerText, '\t');
 | 
						|
      assert.equal(
 | 
						|
          builder._formatText(html, tabSize, Infinity).innerHTML,
 | 
						|
          'abc' + wrapper.outerHTML + 'def');
 | 
						|
    });
 | 
						|
 | 
						|
    test('tab wrapper style', () => {
 | 
						|
      const pattern = new RegExp('^<span class="style-scope gr-diff tab" '
 | 
						|
          + 'style="(?:-moz-)?tab-size: (\\d+);">\\t<\\/span>$');
 | 
						|
 | 
						|
      for (const size of [1, 3, 8, 55]) {
 | 
						|
        const html = builder._getTabWrapper(size).outerHTML;
 | 
						|
        expect(html).to.match(pattern);
 | 
						|
        assert.equal(html.match(pattern)[1], size);
 | 
						|
      }
 | 
						|
    });
 | 
						|
 | 
						|
    test('_handlePreferenceError called with invalid preference', () => {
 | 
						|
      sandbox.stub(element, '_handlePreferenceError');
 | 
						|
      const prefs = {tab_size: 0};
 | 
						|
      element._getDiffBuilder(element.diff, prefs);
 | 
						|
      assert.isTrue(element._handlePreferenceError.lastCall
 | 
						|
          .calledWithExactly('tab size'));
 | 
						|
    });
 | 
						|
 | 
						|
    test('_handlePreferenceError triggers alert and javascript error', () => {
 | 
						|
      const errorStub = sinon.stub();
 | 
						|
      element.addEventListener('show-alert', errorStub);
 | 
						|
      assert.throws(element._handlePreferenceError.bind(element, 'tab size'));
 | 
						|
      assert.equal(errorStub.lastCall.args[0].detail.message,
 | 
						|
          `The value of the 'tab size' user preference is invalid. ` +
 | 
						|
        `Fix in diff preferences`);
 | 
						|
    });
 | 
						|
 | 
						|
    suite('_isTotal', () => {
 | 
						|
      test('is total for add', () => {
 | 
						|
        const group = new GrDiffGroup(GrDiffGroup.Type.DELTA);
 | 
						|
        for (let idx = 0; idx < 10; idx++) {
 | 
						|
          group.addLine(new GrDiffLine(GrDiffLine.Type.ADD));
 | 
						|
        }
 | 
						|
        assert.isTrue(GrDiffBuilder.prototype._isTotal(group));
 | 
						|
      });
 | 
						|
 | 
						|
      test('is total for remove', () => {
 | 
						|
        const group = new GrDiffGroup(GrDiffGroup.Type.DELTA);
 | 
						|
        for (let idx = 0; idx < 10; idx++) {
 | 
						|
          group.addLine(new GrDiffLine(GrDiffLine.Type.REMOVE));
 | 
						|
        }
 | 
						|
        assert.isTrue(GrDiffBuilder.prototype._isTotal(group));
 | 
						|
      });
 | 
						|
 | 
						|
      test('not total for empty', () => {
 | 
						|
        const group = new GrDiffGroup(GrDiffGroup.Type.BOTH);
 | 
						|
        assert.isFalse(GrDiffBuilder.prototype._isTotal(group));
 | 
						|
      });
 | 
						|
 | 
						|
      test('not total for non-delta', () => {
 | 
						|
        const group = new GrDiffGroup(GrDiffGroup.Type.DELTA);
 | 
						|
        for (let idx = 0; idx < 10; idx++) {
 | 
						|
          group.addLine(new GrDiffLine(GrDiffLine.Type.BOTH));
 | 
						|
        }
 | 
						|
        assert.isFalse(GrDiffBuilder.prototype._isTotal(group));
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    suite('intraline differences', () => {
 | 
						|
      let el;
 | 
						|
      let str;
 | 
						|
      let annotateElementSpy;
 | 
						|
      let layer;
 | 
						|
      const lineNumberEl = document.createElement('td');
 | 
						|
 | 
						|
      function slice(str, start, end) {
 | 
						|
        return Array.from(str).slice(start, end).join('');
 | 
						|
      }
 | 
						|
 | 
						|
      setup(() => {
 | 
						|
        el = fixture('div-with-text');
 | 
						|
        str = el.textContent;
 | 
						|
        annotateElementSpy = sandbox.spy(GrAnnotation, 'annotateElement');
 | 
						|
        layer = document.createElement('gr-diff-builder')
 | 
						|
            ._createIntralineLayer();
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotate no highlights', () => {
 | 
						|
        const line = {
 | 
						|
          text: str,
 | 
						|
          highlights: [],
 | 
						|
        };
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        // The content is unchanged.
 | 
						|
        assert.isFalse(annotateElementSpy.called);
 | 
						|
        assert.equal(el.childNodes.length, 1);
 | 
						|
        assert.instanceOf(el.childNodes[0], Text);
 | 
						|
        assert.equal(str, el.childNodes[0].textContent);
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotate with highlights', () => {
 | 
						|
        const line = {
 | 
						|
          text: str,
 | 
						|
          highlights: [
 | 
						|
            {startIndex: 6, endIndex: 12},
 | 
						|
            {startIndex: 18, endIndex: 22},
 | 
						|
          ],
 | 
						|
        };
 | 
						|
        const str0 = slice(str, 0, 6);
 | 
						|
        const str1 = slice(str, 6, 12);
 | 
						|
        const str2 = slice(str, 12, 18);
 | 
						|
        const str3 = slice(str, 18, 22);
 | 
						|
        const str4 = slice(str, 22);
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.isTrue(annotateElementSpy.called);
 | 
						|
        assert.equal(el.childNodes.length, 5);
 | 
						|
 | 
						|
        assert.instanceOf(el.childNodes[0], Text);
 | 
						|
        assert.equal(el.childNodes[0].textContent, str0);
 | 
						|
 | 
						|
        assert.notInstanceOf(el.childNodes[1], Text);
 | 
						|
        assert.equal(el.childNodes[1].textContent, str1);
 | 
						|
 | 
						|
        assert.instanceOf(el.childNodes[2], Text);
 | 
						|
        assert.equal(el.childNodes[2].textContent, str2);
 | 
						|
 | 
						|
        assert.notInstanceOf(el.childNodes[3], Text);
 | 
						|
        assert.equal(el.childNodes[3].textContent, str3);
 | 
						|
 | 
						|
        assert.instanceOf(el.childNodes[4], Text);
 | 
						|
        assert.equal(el.childNodes[4].textContent, str4);
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotate without endIndex', () => {
 | 
						|
        const line = {
 | 
						|
          text: str,
 | 
						|
          highlights: [
 | 
						|
            {startIndex: 28},
 | 
						|
          ],
 | 
						|
        };
 | 
						|
 | 
						|
        const str0 = slice(str, 0, 28);
 | 
						|
        const str1 = slice(str, 28);
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.isTrue(annotateElementSpy.called);
 | 
						|
        assert.equal(el.childNodes.length, 2);
 | 
						|
 | 
						|
        assert.instanceOf(el.childNodes[0], Text);
 | 
						|
        assert.equal(el.childNodes[0].textContent, str0);
 | 
						|
 | 
						|
        assert.notInstanceOf(el.childNodes[1], Text);
 | 
						|
        assert.equal(el.childNodes[1].textContent, str1);
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotate ignores empty highlights', () => {
 | 
						|
        const line = {
 | 
						|
          text: str,
 | 
						|
          highlights: [
 | 
						|
            {startIndex: 28, endIndex: 28},
 | 
						|
          ],
 | 
						|
        };
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.isFalse(annotateElementSpy.called);
 | 
						|
        assert.equal(el.childNodes.length, 1);
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotate handles unicode', () => {
 | 
						|
        // Put some unicode into the string:
 | 
						|
        str = str.replace(/\s/g, '💢');
 | 
						|
        el.textContent = str;
 | 
						|
        const line = {
 | 
						|
          text: str,
 | 
						|
          highlights: [
 | 
						|
            {startIndex: 6, endIndex: 12},
 | 
						|
          ],
 | 
						|
        };
 | 
						|
 | 
						|
        const str0 = slice(str, 0, 6);
 | 
						|
        const str1 = slice(str, 6, 12);
 | 
						|
        const str2 = slice(str, 12);
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.isTrue(annotateElementSpy.called);
 | 
						|
        assert.equal(el.childNodes.length, 3);
 | 
						|
 | 
						|
        assert.instanceOf(el.childNodes[0], Text);
 | 
						|
        assert.equal(el.childNodes[0].textContent, str0);
 | 
						|
 | 
						|
        assert.notInstanceOf(el.childNodes[1], Text);
 | 
						|
        assert.equal(el.childNodes[1].textContent, str1);
 | 
						|
 | 
						|
        assert.instanceOf(el.childNodes[2], Text);
 | 
						|
        assert.equal(el.childNodes[2].textContent, str2);
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotate handles unicode w/o endIndex', () => {
 | 
						|
        // Put some unicode into the string:
 | 
						|
        str = str.replace(/\s/g, '💢');
 | 
						|
        el.textContent = str;
 | 
						|
 | 
						|
        const line = {
 | 
						|
          text: str,
 | 
						|
          highlights: [
 | 
						|
            {startIndex: 6},
 | 
						|
          ],
 | 
						|
        };
 | 
						|
 | 
						|
        const str0 = slice(str, 0, 6);
 | 
						|
        const str1 = slice(str, 6);
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.isTrue(annotateElementSpy.called);
 | 
						|
        assert.equal(el.childNodes.length, 2);
 | 
						|
 | 
						|
        assert.instanceOf(el.childNodes[0], Text);
 | 
						|
        assert.equal(el.childNodes[0].textContent, str0);
 | 
						|
 | 
						|
        assert.notInstanceOf(el.childNodes[1], Text);
 | 
						|
        assert.equal(el.childNodes[1].textContent, str1);
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    suite('tab indicators', () => {
 | 
						|
      let element;
 | 
						|
      let layer;
 | 
						|
      const lineNumberEl = document.createElement('td');
 | 
						|
 | 
						|
      setup(() => {
 | 
						|
        element = fixture('basic');
 | 
						|
        element._showTabs = true;
 | 
						|
        layer = element._createTabIndicatorLayer();
 | 
						|
      });
 | 
						|
 | 
						|
      test('does nothing with empty line', () => {
 | 
						|
        const line = {text: ''};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.isFalse(annotateElementStub.called);
 | 
						|
      });
 | 
						|
 | 
						|
      test('does nothing with no tabs', () => {
 | 
						|
        const str = 'lorem ipsum no tabs';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.isFalse(annotateElementStub.called);
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotates tab at beginning', () => {
 | 
						|
        const str = '\tlorem upsum';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.equal(annotateElementStub.callCount, 1);
 | 
						|
        const args = annotateElementStub.getCalls()[0].args;
 | 
						|
        assert.equal(args[0], el);
 | 
						|
        assert.equal(args[1], 0, 'offset of tab indicator');
 | 
						|
        assert.equal(args[2], 1, 'length of tab indicator');
 | 
						|
        assert.include(args[3], 'tab-indicator');
 | 
						|
      });
 | 
						|
 | 
						|
      test('does not annotate when disabled', () => {
 | 
						|
        element._showTabs = false;
 | 
						|
 | 
						|
        const str = '\tlorem upsum';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.isFalse(annotateElementStub.called);
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotates multiple in beginning', () => {
 | 
						|
        const str = '\t\tlorem upsum';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.equal(annotateElementStub.callCount, 2);
 | 
						|
 | 
						|
        let args = annotateElementStub.getCalls()[0].args;
 | 
						|
        assert.equal(args[0], el);
 | 
						|
        assert.equal(args[1], 0, 'offset of tab indicator');
 | 
						|
        assert.equal(args[2], 1, 'length of tab indicator');
 | 
						|
        assert.include(args[3], 'tab-indicator');
 | 
						|
 | 
						|
        args = annotateElementStub.getCalls()[1].args;
 | 
						|
        assert.equal(args[0], el);
 | 
						|
        assert.equal(args[1], 1, 'offset of tab indicator');
 | 
						|
        assert.equal(args[2], 1, 'length of tab indicator');
 | 
						|
        assert.include(args[3], 'tab-indicator');
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotates intermediate tabs', () => {
 | 
						|
        const str = 'lorem\tupsum';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
 | 
						|
        assert.equal(annotateElementStub.callCount, 1);
 | 
						|
        const args = annotateElementStub.getCalls()[0].args;
 | 
						|
        assert.equal(args[0], el);
 | 
						|
        assert.equal(args[1], 5, 'offset of tab indicator');
 | 
						|
        assert.equal(args[2], 1, 'length of tab indicator');
 | 
						|
        assert.include(args[3], 'tab-indicator');
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    suite('layers', () => {
 | 
						|
      let element;
 | 
						|
      let initialLayersCount;
 | 
						|
      let withLayerCount;
 | 
						|
      setup(() => {
 | 
						|
        const layers = [];
 | 
						|
        element = fixture('basic');
 | 
						|
        element.layers = layers;
 | 
						|
        element._showTrailingWhitespace = true;
 | 
						|
        element._setupAnnotationLayers();
 | 
						|
        initialLayersCount = element._layers.length;
 | 
						|
      });
 | 
						|
 | 
						|
      test('no layers', () => {
 | 
						|
        element._setupAnnotationLayers();
 | 
						|
        assert.equal(element._layers.length, initialLayersCount);
 | 
						|
      });
 | 
						|
 | 
						|
      suite('with layers', () => {
 | 
						|
        const layers = [{}, {}];
 | 
						|
        setup(() => {
 | 
						|
          element = fixture('basic');
 | 
						|
          element.layers = layers;
 | 
						|
          element._showTrailingWhitespace = true;
 | 
						|
          element._setupAnnotationLayers();
 | 
						|
          withLayerCount = element._layers.length;
 | 
						|
        });
 | 
						|
        test('with layers', () => {
 | 
						|
          element._setupAnnotationLayers();
 | 
						|
          assert.equal(element._layers.length, withLayerCount);
 | 
						|
          assert.equal(initialLayersCount + layers.length,
 | 
						|
              withLayerCount);
 | 
						|
        });
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    suite('trailing whitespace', () => {
 | 
						|
      let element;
 | 
						|
      let layer;
 | 
						|
      const lineNumberEl = document.createElement('td');
 | 
						|
 | 
						|
      setup(() => {
 | 
						|
        element = fixture('basic');
 | 
						|
        element._showTrailingWhitespace = true;
 | 
						|
        layer = element._createTrailingWhitespaceLayer();
 | 
						|
      });
 | 
						|
 | 
						|
      test('does nothing with empty line', () => {
 | 
						|
        const line = {text: ''};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
        assert.isFalse(annotateElementStub.called);
 | 
						|
      });
 | 
						|
 | 
						|
      test('does nothing with no trailing whitespace', () => {
 | 
						|
        const str = 'lorem ipsum blah blah';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
        assert.isFalse(annotateElementStub.called);
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotates trailing spaces', () => {
 | 
						|
        const str = 'lorem ipsum   ';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
        assert.isTrue(annotateElementStub.called);
 | 
						|
        assert.equal(annotateElementStub.lastCall.args[1], 11);
 | 
						|
        assert.equal(annotateElementStub.lastCall.args[2], 3);
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotates trailing tabs', () => {
 | 
						|
        const str = 'lorem ipsum\t\t\t';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
        assert.isTrue(annotateElementStub.called);
 | 
						|
        assert.equal(annotateElementStub.lastCall.args[1], 11);
 | 
						|
        assert.equal(annotateElementStub.lastCall.args[2], 3);
 | 
						|
      });
 | 
						|
 | 
						|
      test('annotates mixed trailing whitespace', () => {
 | 
						|
        const str = 'lorem ipsum\t \t';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
        assert.isTrue(annotateElementStub.called);
 | 
						|
        assert.equal(annotateElementStub.lastCall.args[1], 11);
 | 
						|
        assert.equal(annotateElementStub.lastCall.args[2], 3);
 | 
						|
      });
 | 
						|
 | 
						|
      test('unicode preceding trailing whitespace', () => {
 | 
						|
        const str = '💢\t';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
        assert.isTrue(annotateElementStub.called);
 | 
						|
        assert.equal(annotateElementStub.lastCall.args[1], 1);
 | 
						|
        assert.equal(annotateElementStub.lastCall.args[2], 1);
 | 
						|
      });
 | 
						|
 | 
						|
      test('does not annotate when disabled', () => {
 | 
						|
        element._showTrailingWhitespace = false;
 | 
						|
        const str = 'lorem upsum\t \t ';
 | 
						|
        const line = {text: str};
 | 
						|
        const el = document.createElement('div');
 | 
						|
        el.textContent = str;
 | 
						|
        const annotateElementStub =
 | 
						|
            sandbox.stub(GrAnnotation, 'annotateElement');
 | 
						|
        layer.annotate(el, lineNumberEl, line);
 | 
						|
        assert.isFalse(annotateElementStub.called);
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    suite('rendering text, images and binary files', () => {
 | 
						|
      let processStub;
 | 
						|
      let keyLocations;
 | 
						|
      let prefs;
 | 
						|
      let content;
 | 
						|
 | 
						|
      setup(() => {
 | 
						|
        element = fixture('basic');
 | 
						|
        element.viewMode = 'SIDE_BY_SIDE';
 | 
						|
        processStub = sandbox.stub(element.$.processor, 'process')
 | 
						|
            .returns(Promise.resolve());
 | 
						|
        keyLocations = {left: {}, right: {}};
 | 
						|
        prefs = {
 | 
						|
          line_length: 10,
 | 
						|
          show_tabs: true,
 | 
						|
          tab_size: 4,
 | 
						|
          context: -1,
 | 
						|
          syntax_highlighting: true,
 | 
						|
        };
 | 
						|
        content = [{
 | 
						|
          a: ['all work and no play make andybons a dull boy'],
 | 
						|
          b: ['elgoog elgoog elgoog'],
 | 
						|
        }, {
 | 
						|
          ab: [
 | 
						|
            'Non eram nescius, Brute, cum, quae summis ingeniis ',
 | 
						|
            'exquisitaque doctrina philosophi Graeco sermone tractavissent',
 | 
						|
          ],
 | 
						|
        }];
 | 
						|
      });
 | 
						|
 | 
						|
      test('text', () => {
 | 
						|
        element.diff = {content};
 | 
						|
        return element.render(keyLocations, prefs).then(() => {
 | 
						|
          assert.isTrue(processStub.calledOnce);
 | 
						|
          assert.isFalse(processStub.lastCall.args[1]);
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      test('image', () => {
 | 
						|
        element.diff = {content, binary: true};
 | 
						|
        element.isImageDiff = true;
 | 
						|
        return element.render(keyLocations, prefs).then(() => {
 | 
						|
          assert.isTrue(processStub.calledOnce);
 | 
						|
          assert.isTrue(processStub.lastCall.args[1]);
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      test('binary', () => {
 | 
						|
        element.diff = {content, binary: true};
 | 
						|
        return element.render(keyLocations, prefs).then(() => {
 | 
						|
          assert.isTrue(processStub.calledOnce);
 | 
						|
          assert.isTrue(processStub.lastCall.args[1]);
 | 
						|
        });
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    suite('rendering', () => {
 | 
						|
      let content;
 | 
						|
      let outputEl;
 | 
						|
      let keyLocations;
 | 
						|
 | 
						|
      setup(done => {
 | 
						|
        const prefs = {
 | 
						|
          line_length: 10,
 | 
						|
          show_tabs: true,
 | 
						|
          tab_size: 4,
 | 
						|
          context: -1,
 | 
						|
          syntax_highlighting: true,
 | 
						|
        };
 | 
						|
        content = [
 | 
						|
          {
 | 
						|
            a: ['all work and no play make andybons a dull boy'],
 | 
						|
            b: ['elgoog elgoog elgoog'],
 | 
						|
          },
 | 
						|
          {
 | 
						|
            ab: [
 | 
						|
              'Non eram nescius, Brute, cum, quae summis ingeniis ',
 | 
						|
              'exquisitaque doctrina philosophi Graeco sermone tractavissent',
 | 
						|
            ],
 | 
						|
          },
 | 
						|
        ];
 | 
						|
        element = fixture('basic');
 | 
						|
        outputEl = element.queryEffectiveChildren('#diffTable');
 | 
						|
        keyLocations = {left: {}, right: {}};
 | 
						|
        sandbox.stub(element, '_getDiffBuilder', () => {
 | 
						|
          const builder = new GrDiffBuilder({content}, prefs, outputEl);
 | 
						|
          sandbox.stub(builder, 'addColumns');
 | 
						|
          builder.buildSectionElement = function(group) {
 | 
						|
            const section = document.createElement('stub');
 | 
						|
            section.textContent = group.lines.reduce((acc, line) => {
 | 
						|
              return acc + line.text;
 | 
						|
            }, '');
 | 
						|
            return section;
 | 
						|
          };
 | 
						|
          return builder;
 | 
						|
        });
 | 
						|
        element.diff = {content};
 | 
						|
        element.render(keyLocations, prefs).then(done);
 | 
						|
      });
 | 
						|
 | 
						|
      test('renderSection', () => {
 | 
						|
        let section = outputEl.querySelector('stub:nth-of-type(2)');
 | 
						|
        const prevInnerHTML = section.innerHTML;
 | 
						|
        section.innerHTML = 'wiped';
 | 
						|
        element._builder.renderSection(section);
 | 
						|
        section = outputEl.querySelector('stub:nth-of-type(2)');
 | 
						|
        assert.equal(section.innerHTML, prevInnerHTML);
 | 
						|
      });
 | 
						|
 | 
						|
      test('addColumns is called', done => {
 | 
						|
        element.render(keyLocations, {}).then(done);
 | 
						|
        assert.isTrue(element._builder.addColumns.called);
 | 
						|
      });
 | 
						|
 | 
						|
      test('getSectionsByLineRange one line', () => {
 | 
						|
        const section = outputEl.querySelector('stub:nth-of-type(2)');
 | 
						|
        const sections = element._builder.getSectionsByLineRange(1, 1, 'left');
 | 
						|
        assert.equal(sections.length, 1);
 | 
						|
        assert.strictEqual(sections[0], section);
 | 
						|
      });
 | 
						|
 | 
						|
      test('getSectionsByLineRange over diff', () => {
 | 
						|
        const section = [
 | 
						|
          outputEl.querySelector('stub:nth-of-type(2)'),
 | 
						|
          outputEl.querySelector('stub:nth-of-type(3)'),
 | 
						|
        ];
 | 
						|
        const sections = element._builder.getSectionsByLineRange(1, 2, 'left');
 | 
						|
        assert.equal(sections.length, 2);
 | 
						|
        assert.strictEqual(sections[0], section[0]);
 | 
						|
        assert.strictEqual(sections[1], section[1]);
 | 
						|
      });
 | 
						|
 | 
						|
      test('render-start and render-content are fired', done => {
 | 
						|
        const dispatchEventStub = sandbox.stub(element, 'dispatchEvent');
 | 
						|
        element.render(keyLocations, {}).then(() => {
 | 
						|
          const firedEventTypes = dispatchEventStub.getCalls()
 | 
						|
              .map(c => { return c.args[0].type; });
 | 
						|
          assert.include(firedEventTypes, 'render-start');
 | 
						|
          assert.include(firedEventTypes, 'render-content');
 | 
						|
          done();
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      test('cancel', () => {
 | 
						|
        const processorCancelStub = sandbox.stub(element.$.processor, 'cancel');
 | 
						|
        element.cancel();
 | 
						|
        assert.isTrue(processorCancelStub.called);
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    suite('mock-diff', () => {
 | 
						|
      let element;
 | 
						|
      let builder;
 | 
						|
      let diff;
 | 
						|
      let prefs;
 | 
						|
      let keyLocations;
 | 
						|
 | 
						|
      setup(done => {
 | 
						|
        element = fixture('mock-diff');
 | 
						|
        diff = document.createElement('mock-diff-response').diffResponse;
 | 
						|
        element.diff = diff;
 | 
						|
 | 
						|
        prefs = {
 | 
						|
          line_length: 80,
 | 
						|
          show_tabs: true,
 | 
						|
          tab_size: 4,
 | 
						|
        };
 | 
						|
        keyLocations = {left: {}, right: {}};
 | 
						|
 | 
						|
        element.render(keyLocations, prefs).then(() => {
 | 
						|
          builder = element._builder;
 | 
						|
          done();
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      test('getContentByLine', () => {
 | 
						|
        let actual;
 | 
						|
 | 
						|
        actual = builder.getContentByLine(2, 'left');
 | 
						|
        assert.equal(actual.textContent, diff.content[0].ab[1]);
 | 
						|
 | 
						|
        actual = builder.getContentByLine(2, 'right');
 | 
						|
        assert.equal(actual.textContent, diff.content[0].ab[1]);
 | 
						|
 | 
						|
        actual = builder.getContentByLine(5, 'left');
 | 
						|
        assert.equal(actual.textContent, diff.content[2].ab[0]);
 | 
						|
 | 
						|
        actual = builder.getContentByLine(5, 'right');
 | 
						|
        assert.equal(actual.textContent, diff.content[1].b[0]);
 | 
						|
      });
 | 
						|
 | 
						|
      test('findLinesByRange', () => {
 | 
						|
        const lines = [];
 | 
						|
        const elems = [];
 | 
						|
        const start = 6;
 | 
						|
        const end = 10;
 | 
						|
        const count = end - start + 1;
 | 
						|
 | 
						|
        builder.findLinesByRange(start, end, 'right', lines, elems);
 | 
						|
 | 
						|
        assert.equal(lines.length, count);
 | 
						|
        assert.equal(elems.length, count);
 | 
						|
 | 
						|
        for (let i = 0; i < 5; i++) {
 | 
						|
          assert.instanceOf(lines[i], GrDiffLine);
 | 
						|
          assert.equal(lines[i].afterNumber, start + i);
 | 
						|
          assert.instanceOf(elems[i], HTMLElement);
 | 
						|
          assert.equal(lines[i].text, elems[i].textContent);
 | 
						|
        }
 | 
						|
      });
 | 
						|
 | 
						|
      test('_renderContentByRange', () => {
 | 
						|
        const spy = sandbox.spy(builder, '_createTextEl');
 | 
						|
        const start = 9;
 | 
						|
        const end = 14;
 | 
						|
        const count = end - start + 1;
 | 
						|
 | 
						|
        builder._renderContentByRange(start, end, 'left');
 | 
						|
 | 
						|
        assert.equal(spy.callCount, count);
 | 
						|
        spy.getCalls().forEach((call, i) => {
 | 
						|
          assert.equal(call.args[1].beforeNumber, start + i);
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      test('_renderContentByRange notexistent elements', () => {
 | 
						|
        const spy = sandbox.spy(builder, '_createTextEl');
 | 
						|
 | 
						|
        sandbox.stub(builder, 'findLinesByRange',
 | 
						|
            (s, e, d, lines, elements) => {
 | 
						|
              // Add a line and a corresponding element.
 | 
						|
              lines.push(new GrDiffLine(GrDiffLine.Type.BOTH));
 | 
						|
              const tr = document.createElement('tr');
 | 
						|
              const td = document.createElement('td');
 | 
						|
              const el = document.createElement('div');
 | 
						|
              tr.appendChild(td);
 | 
						|
              td.appendChild(el);
 | 
						|
              elements.push(el);
 | 
						|
 | 
						|
              // Add 2 lines without corresponding elements.
 | 
						|
              lines.push(new GrDiffLine(GrDiffLine.Type.BOTH));
 | 
						|
              lines.push(new GrDiffLine(GrDiffLine.Type.BOTH));
 | 
						|
            });
 | 
						|
 | 
						|
        builder._renderContentByRange(1, 10, 'left');
 | 
						|
        // Should be called only once because only one line had a corresponding
 | 
						|
        // element.
 | 
						|
        assert.equal(spy.callCount, 1);
 | 
						|
      });
 | 
						|
 | 
						|
      test('_getLineNumberEl side-by-side left', () => {
 | 
						|
        const contentEl = builder.getContentByLine(5, 'left',
 | 
						|
            element.$.diffTable);
 | 
						|
        const lineNumberEl = builder._getLineNumberEl(contentEl, 'left');
 | 
						|
        assert.isTrue(lineNumberEl.classList.contains('lineNum'));
 | 
						|
        assert.isTrue(lineNumberEl.classList.contains('left'));
 | 
						|
      });
 | 
						|
 | 
						|
      test('_getLineNumberEl side-by-side right', () => {
 | 
						|
        const contentEl = builder.getContentByLine(5, 'right',
 | 
						|
            element.$.diffTable);
 | 
						|
        const lineNumberEl = builder._getLineNumberEl(contentEl, 'right');
 | 
						|
        assert.isTrue(lineNumberEl.classList.contains('lineNum'));
 | 
						|
        assert.isTrue(lineNumberEl.classList.contains('right'));
 | 
						|
      });
 | 
						|
 | 
						|
      test('_getLineNumberEl unified left', done => {
 | 
						|
        // Re-render as unified:
 | 
						|
        element.viewMode = 'UNIFIED_DIFF';
 | 
						|
        element.render(keyLocations, prefs).then(() => {
 | 
						|
          builder = element._builder;
 | 
						|
 | 
						|
          const contentEl = builder.getContentByLine(5, 'left',
 | 
						|
              element.$.diffTable);
 | 
						|
          const lineNumberEl = builder._getLineNumberEl(contentEl, 'left');
 | 
						|
          assert.isTrue(lineNumberEl.classList.contains('lineNum'));
 | 
						|
          assert.isTrue(lineNumberEl.classList.contains('left'));
 | 
						|
          done();
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      test('_getLineNumberEl unified right', done => {
 | 
						|
        // Re-render as unified:
 | 
						|
        element.viewMode = 'UNIFIED_DIFF';
 | 
						|
        element.render(keyLocations, prefs).then(() => {
 | 
						|
          builder = element._builder;
 | 
						|
 | 
						|
          const contentEl = builder.getContentByLine(5, 'right',
 | 
						|
              element.$.diffTable);
 | 
						|
          const lineNumberEl = builder._getLineNumberEl(contentEl, 'right');
 | 
						|
          assert.isTrue(lineNumberEl.classList.contains('lineNum'));
 | 
						|
          assert.isTrue(lineNumberEl.classList.contains('right'));
 | 
						|
          done();
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      test('_getNextContentOnSide side-by-side left', () => {
 | 
						|
        const startElem = builder.getContentByLine(5, 'left',
 | 
						|
            element.$.diffTable);
 | 
						|
        const expectedStartString = diff.content[2].ab[0];
 | 
						|
        const expectedNextString = diff.content[2].ab[1];
 | 
						|
        assert.equal(startElem.textContent, expectedStartString);
 | 
						|
 | 
						|
        const nextElem = builder._getNextContentOnSide(startElem,
 | 
						|
            'left');
 | 
						|
        assert.equal(nextElem.textContent, expectedNextString);
 | 
						|
      });
 | 
						|
 | 
						|
      test('_getNextContentOnSide side-by-side right', () => {
 | 
						|
        const startElem = builder.getContentByLine(5, 'right',
 | 
						|
            element.$.diffTable);
 | 
						|
        const expectedStartString = diff.content[1].b[0];
 | 
						|
        const expectedNextString = diff.content[1].b[1];
 | 
						|
        assert.equal(startElem.textContent, expectedStartString);
 | 
						|
 | 
						|
        const nextElem = builder._getNextContentOnSide(startElem,
 | 
						|
            'right');
 | 
						|
        assert.equal(nextElem.textContent, expectedNextString);
 | 
						|
      });
 | 
						|
 | 
						|
      test('_getNextContentOnSide unified left', done => {
 | 
						|
        // Re-render as unified:
 | 
						|
        element.viewMode = 'UNIFIED_DIFF';
 | 
						|
        element.render(keyLocations, prefs).then(() => {
 | 
						|
          builder = element._builder;
 | 
						|
 | 
						|
          const startElem = builder.getContentByLine(5, 'left',
 | 
						|
              element.$.diffTable);
 | 
						|
          const expectedStartString = diff.content[2].ab[0];
 | 
						|
          const expectedNextString = diff.content[2].ab[1];
 | 
						|
          assert.equal(startElem.textContent, expectedStartString);
 | 
						|
 | 
						|
          const nextElem = builder._getNextContentOnSide(startElem,
 | 
						|
              'left');
 | 
						|
          assert.equal(nextElem.textContent, expectedNextString);
 | 
						|
 | 
						|
          done();
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      test('_getNextContentOnSide unified right', done => {
 | 
						|
        // Re-render as unified:
 | 
						|
        element.viewMode = 'UNIFIED_DIFF';
 | 
						|
        element.render(keyLocations, prefs).then(() => {
 | 
						|
          builder = element._builder;
 | 
						|
 | 
						|
          const startElem = builder.getContentByLine(5, 'right',
 | 
						|
              element.$.diffTable);
 | 
						|
          const expectedStartString = diff.content[1].b[0];
 | 
						|
          const expectedNextString = diff.content[1].b[1];
 | 
						|
          assert.equal(startElem.textContent, expectedStartString);
 | 
						|
 | 
						|
          const nextElem = builder._getNextContentOnSide(startElem,
 | 
						|
              'right');
 | 
						|
          assert.equal(nextElem.textContent, expectedNextString);
 | 
						|
 | 
						|
          done();
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
      test('escaping HTML', () => {
 | 
						|
        let input = '<script>alert("XSS");<' + '/script>';
 | 
						|
        let expected = '<script>alert("XSS");</script>';
 | 
						|
        let result = builder._formatText(input, 1, Infinity).innerHTML;
 | 
						|
        assert.equal(result, expected);
 | 
						|
 | 
						|
        input = '& < > " \' / `';
 | 
						|
        expected = '& < > " \' / `';
 | 
						|
        result = builder._formatText(input, 1, Infinity).innerHTML;
 | 
						|
        assert.equal(result, expected);
 | 
						|
      });
 | 
						|
    });
 | 
						|
 | 
						|
    suite('blame', () => {
 | 
						|
      let mockBlame;
 | 
						|
 | 
						|
      setup(() => {
 | 
						|
        mockBlame = [
 | 
						|
          {id: 'commit 1', ranges: [{start: 1, end: 2}, {start: 10, end: 16}]},
 | 
						|
          {id: 'commit 2', ranges: [{start: 4, end: 10}, {start: 17, end: 32}]},
 | 
						|
        ];
 | 
						|
      });
 | 
						|
 | 
						|
      test('setBlame attempts to render each blamed line', () => {
 | 
						|
        const getBlameStub = sandbox.stub(builder, '_getBlameByLineNum')
 | 
						|
            .returns(null);
 | 
						|
        builder.setBlame(mockBlame);
 | 
						|
        assert.equal(getBlameStub.callCount, 32);
 | 
						|
      });
 | 
						|
 | 
						|
      test('_getBlameCommitForBaseLine', () => {
 | 
						|
        builder.setBlame(mockBlame);
 | 
						|
        assert.isOk(builder._getBlameCommitForBaseLine(1));
 | 
						|
        assert.equal(builder._getBlameCommitForBaseLine(1).id, 'commit 1');
 | 
						|
 | 
						|
        assert.isOk(builder._getBlameCommitForBaseLine(11));
 | 
						|
        assert.equal(builder._getBlameCommitForBaseLine(11).id, 'commit 1');
 | 
						|
 | 
						|
        assert.isOk(builder._getBlameCommitForBaseLine(32));
 | 
						|
        assert.equal(builder._getBlameCommitForBaseLine(32).id, 'commit 2');
 | 
						|
 | 
						|
        assert.isNull(builder._getBlameCommitForBaseLine(33));
 | 
						|
      });
 | 
						|
 | 
						|
      test('_getBlameCommitForBaseLine w/o blame returns null', () => {
 | 
						|
        assert.isNull(builder._getBlameCommitForBaseLine(1));
 | 
						|
        assert.isNull(builder._getBlameCommitForBaseLine(11));
 | 
						|
        assert.isNull(builder._getBlameCommitForBaseLine(31));
 | 
						|
      });
 | 
						|
 | 
						|
      test('_createBlameCell', () => {
 | 
						|
        const mocbBlameCell = document.createElement('span');
 | 
						|
        const getBlameStub = sinon.stub(builder, '_getBlameForBaseLine')
 | 
						|
            .returns(mocbBlameCell);
 | 
						|
        const line = new GrDiffLine(GrDiffLine.Type.BOTH);
 | 
						|
        line.beforeNumber = 3;
 | 
						|
        line.afterNumber = 5;
 | 
						|
 | 
						|
        const result = builder._createBlameCell(line);
 | 
						|
 | 
						|
        assert.isTrue(getBlameStub.calledWithExactly(3));
 | 
						|
        assert.equal(result.getAttribute('data-line-number'), '3');
 | 
						|
        assert.equal(result.firstChild, mocbBlameCell);
 | 
						|
      });
 | 
						|
    });
 | 
						|
  });
 | 
						|
</script>
 |