
Potentially related: https://github.com/Polymer/web-component-tester/issues/505 Bug: Issue 5792 Change-Id: I9ab6e8e40d9811dd52906335426764c052907609
557 lines
20 KiB
HTML
557 lines
20 KiB
HTML
<!DOCTYPE html>
|
|
<!--
|
|
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="../../../bower_components/iron-test-helpers/iron-test-helpers.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="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', function() {
|
|
var element;
|
|
var sandbox;
|
|
|
|
setup(function() {
|
|
sandbox = sinon.sandbox.create();
|
|
element = fixture('basic')[1];
|
|
});
|
|
|
|
teardown(function() {
|
|
sandbox.restore();
|
|
});
|
|
|
|
suite('selectionchange event handling', function() {
|
|
var emulateSelection = function() {
|
|
document.dispatchEvent(new CustomEvent('selectionchange'));
|
|
element.flushDebouncer('selectionChange');
|
|
element.flushDebouncer('removeActionBox');
|
|
};
|
|
|
|
setup(function() {
|
|
sandbox.stub(element, '_handleSelection');
|
|
sandbox.stub(element, '_removeActionBox');
|
|
});
|
|
|
|
test('enabled if logged in', function() {
|
|
element.loggedIn = true;
|
|
emulateSelection();
|
|
assert.isTrue(element._handleSelection.called);
|
|
assert.isTrue(element._removeActionBox.called);
|
|
});
|
|
|
|
test('ignored if logged out', function() {
|
|
element.loggedIn = false;
|
|
emulateSelection();
|
|
assert.isFalse(element._handleSelection.called);
|
|
assert.isFalse(element._removeActionBox.called);
|
|
});
|
|
});
|
|
|
|
suite('comment events', function() {
|
|
var builder;
|
|
|
|
setup(function() {
|
|
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', function() {
|
|
sandbox.stub(element, 'set');
|
|
element.fire('comment-mouse-over', {comment: {}});
|
|
assert.isFalse(element.set.called);
|
|
});
|
|
|
|
test('comment-mouse-over from ranged comment causes set', function() {
|
|
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', function() {
|
|
element.fire('comment-mouse-over', {comment: {}});
|
|
assert.isFalse(builder.getContentsByLineRange.called);
|
|
});
|
|
|
|
test('on create-comment action box is removed', function() {
|
|
sandbox.stub(element, '_removeActionBox');
|
|
element.fire('create-comment', {
|
|
comment: {
|
|
range: {},
|
|
},
|
|
});
|
|
assert.isTrue(element._removeActionBox.called);
|
|
});
|
|
});
|
|
|
|
suite('selection', function() {
|
|
var diff;
|
|
var builder;
|
|
var contentStubs;
|
|
|
|
var stubContent = function(line, side, opt_child) {
|
|
var contentTd = diff.querySelector(
|
|
'.' + side + '.lineNum[data-value="' + line + '"] ~ .content');
|
|
var contentText = contentTd.querySelector('.contentText');
|
|
var lineEl = diff.querySelector(
|
|
'.' + side + '.lineNum[data-value="' + line + '"]');
|
|
contentStubs.push({
|
|
lineEl: lineEl,
|
|
contentTd: contentTd,
|
|
contentText: 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;
|
|
};
|
|
|
|
var emulateSelection = function(
|
|
startNode, startOffset, endNode, endOffset) {
|
|
var selection = window.getSelection();
|
|
var range = document.createRange();
|
|
range.setStart(startNode, startOffset);
|
|
range.setEnd(endNode, endOffset);
|
|
selection.addRange(range);
|
|
element._handleSelection();
|
|
};
|
|
|
|
var getActionRange = function() {
|
|
return Polymer.dom(element.root).querySelector(
|
|
'gr-selection-action-box').range;
|
|
};
|
|
|
|
var getActionSide = function() {
|
|
return Polymer.dom(element.root).querySelector(
|
|
'gr-selection-action-box').side;
|
|
};
|
|
|
|
var getLineElByChild = function(node) {
|
|
var stubs = contentStubs.find(function(stub) {
|
|
return stub.contentTd.contains(node);
|
|
});
|
|
return stubs && stubs.lineEl;
|
|
};
|
|
|
|
setup(function() {
|
|
contentStubs = [];
|
|
stub('gr-selection-action-box', {
|
|
placeAbove: sandbox.stub(),
|
|
});
|
|
diff = element.querySelector('#diffTable');
|
|
builder = {
|
|
getContentByLine: sandbox.stub(),
|
|
getContentByLineEl: sandbox.stub(),
|
|
getLineElByChild: getLineElByChild,
|
|
getLineNumberByChild: sandbox.stub(),
|
|
getSideByLineEl: sandbox.stub(),
|
|
};
|
|
element._cachedDiffBuilder = builder;
|
|
});
|
|
|
|
teardown(function() {
|
|
contentStubs = null;
|
|
window.getSelection().removeAllRanges();
|
|
});
|
|
|
|
test('single line', function() {
|
|
var content = stubContent(138, 'left');
|
|
emulateSelection(content.firstChild, 5, content.firstChild, 12);
|
|
assert.isTrue(element.isRangeSelected());
|
|
assert.deepEqual(getActionRange(), {
|
|
startLine: 138,
|
|
startChar: 5,
|
|
endLine: 138,
|
|
endChar: 12,
|
|
});
|
|
assert.equal(getActionSide(), 'left');
|
|
});
|
|
|
|
test('multiline', function() {
|
|
var startContent = stubContent(119, 'right');
|
|
var endContent = stubContent(120, 'right');
|
|
emulateSelection(
|
|
startContent.firstChild, 10, endContent.lastChild, 7);
|
|
assert.isTrue(element.isRangeSelected());
|
|
assert.deepEqual(getActionRange(), {
|
|
startLine: 119,
|
|
startChar: 10,
|
|
endLine: 120,
|
|
endChar: 36,
|
|
});
|
|
assert.equal(getActionSide(), 'right');
|
|
});
|
|
|
|
test('multiline grow end highlight over tabs', function() {
|
|
var startContent = stubContent(119, 'right');
|
|
var 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', function() {
|
|
var 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', function() {
|
|
var content = stubContent(140, 'left');
|
|
var 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', function() {
|
|
var content = stubContent(140, 'left');
|
|
var 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', function() {
|
|
var content = stubContent(140, 'left');
|
|
var 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', function() {
|
|
var contentText = stubContent(140, 'left');
|
|
var contentTd = contentText.parentElement;
|
|
|
|
emulateSelection(contentTd.previousElementSibling, 0,
|
|
contentText.firstChild, 2);
|
|
assert.isFalse(element.isRangeSelected());
|
|
});
|
|
|
|
test('ends outside of diff', function() {
|
|
var content = stubContent(140, 'left');
|
|
emulateSelection(content.nextElementSibling.firstChild, 2,
|
|
content.firstChild, 2);
|
|
assert.isFalse(element.isRangeSelected());
|
|
});
|
|
|
|
test('starts and ends on different sides', function() {
|
|
var startContent = stubContent(140, 'left');
|
|
var endContent = stubContent(130, 'right');
|
|
emulateSelection(startContent.firstChild, 2, endContent.firstChild, 2);
|
|
assert.isFalse(element.isRangeSelected());
|
|
});
|
|
|
|
test('starts in comment thread element', function() {
|
|
var startContent = stubContent(140, 'left');
|
|
var comment = startContent.parentElement.querySelector(
|
|
'gr-diff-comment-thread');
|
|
var 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', function() {
|
|
var content = stubContent(140, 'left');
|
|
var 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', function() {
|
|
var contextControl =
|
|
diff.querySelector('.contextControl').querySelector('gr-button');
|
|
var content = stubContent(146, 'right');
|
|
emulateSelection(contextControl, 0, content.firstChild, 7);
|
|
// TODO (viktard): Select nearest line.
|
|
assert.isFalse(element.isRangeSelected());
|
|
});
|
|
|
|
test('ends in context element', function() {
|
|
var contextControl =
|
|
diff.querySelector('.contextControl').querySelector('gr-button');
|
|
var content = stubContent(141, 'left');
|
|
emulateSelection(content.firstChild, 2, contextControl, 1);
|
|
// TODO (viktard): Select nearest line.
|
|
assert.isFalse(element.isRangeSelected());
|
|
});
|
|
|
|
test('selection containing context element', function() {
|
|
var startContent = stubContent(130, 'right');
|
|
var 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', function() {
|
|
var 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', function() {
|
|
var 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', function() {
|
|
var content = stubContent(140, 'left');
|
|
var spy = sinon.spy(element, '_normalizeRange');
|
|
emulateSelection(
|
|
content.querySelectorAll('hl')[3], 0,
|
|
content.querySelectorAll('span')[1], 0);
|
|
var spyCall = spy.getCall(0);
|
|
var range = window.getSelection().getRangeAt(0);
|
|
assert.notDeepEqual(spyCall.returnValue, range);
|
|
});
|
|
|
|
test('GrRangeNormalizer._getTextOffset computes text offset', function() {
|
|
var content = stubContent(140, 'left');
|
|
var child = content.lastChild.lastChild;
|
|
var 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', function() {
|
|
test('_fixTripleClickSelection', function() {
|
|
var fakeRange = {
|
|
startContainer: '',
|
|
startOffset: '',
|
|
endContainer: '',
|
|
endOffset: ''
|
|
};
|
|
var 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', function() {
|
|
var startNode = stubContent(138, 'left');
|
|
var 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', function() {
|
|
var startNode = stubContent(119, 'right');
|
|
var endNode =
|
|
stubContent(140, 'left').parentElement.previousElementSibling;
|
|
emulateSelection(startNode, 0, endNode, 0);
|
|
assert.deepEqual(getActionRange(), {
|
|
startLine: 119,
|
|
startChar: 0,
|
|
endLine: 119,
|
|
endChar: 63,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
</script>
|