
In lieu of upstream fixes to Highlight.js, write local workarounds for some known parsing bugs. Bug: Issue 4864 Bug: Issue 4776 Change-Id: I6bde7d44821df18a07ea45dbbc2a7343d597963d
445 lines
15 KiB
HTML
445 lines
15 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-syntax-layer</title>
|
|
|
|
<script src="../../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
|
|
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
|
|
|
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
|
|
<link rel="import" href="gr-syntax-layer.html">
|
|
|
|
<test-fixture id="basic">
|
|
<template>
|
|
<gr-syntax-layer></gr-syntax-layer>
|
|
</template>
|
|
</test-fixture>
|
|
|
|
<script>
|
|
suite('gr-syntax-layer tests', function() {
|
|
var sandbox;
|
|
var diff;
|
|
var element;
|
|
|
|
function getMockHLJS() {
|
|
var html = '<span class="gr-diff gr-syntax gr-syntax-string">' +
|
|
'ipsum</span>';
|
|
return {
|
|
configure: function() {},
|
|
highlight: function(lang, line, ignore, state) {
|
|
return {
|
|
value: line.replace(/ipsum/, html),
|
|
top: state === undefined ? 1 : state + 1,
|
|
};
|
|
},
|
|
};
|
|
}
|
|
|
|
setup(function() {
|
|
sandbox = sinon.sandbox.create();
|
|
element = fixture('basic');
|
|
var mock = document.createElement('mock-diff-response');
|
|
diff = mock.diffResponse;
|
|
element.diff = diff;
|
|
});
|
|
|
|
teardown(function() {
|
|
sandbox.restore();
|
|
});
|
|
|
|
test('annotate without range does nothing', function() {
|
|
var annotationSpy = sandbox.spy(GrAnnotation, 'annotateElement');
|
|
var el = document.createElement('div');
|
|
el.textContent = 'Etiam dui, blandit wisi.';
|
|
var line = new GrDiffLine(GrDiffLine.Type.REMOVE);
|
|
line.beforeNumber = 12;
|
|
|
|
element.annotate(el, line);
|
|
|
|
assert.isFalse(annotationSpy.called);
|
|
});
|
|
|
|
test('annotate with range applies it', function() {
|
|
var str = 'Etiam dui, blandit wisi.';
|
|
var start = 6;
|
|
var length = 3;
|
|
var className = 'foobar';
|
|
|
|
var annotationSpy = sandbox.spy(GrAnnotation, 'annotateElement');
|
|
var el = document.createElement('div');
|
|
el.textContent = str;
|
|
var line = new GrDiffLine(GrDiffLine.Type.REMOVE);
|
|
line.beforeNumber = 12;
|
|
element._baseRanges[11] = [{
|
|
start: start,
|
|
length: length,
|
|
className: className,
|
|
}];
|
|
|
|
element.annotate(el, line);
|
|
|
|
assert.isTrue(annotationSpy.called);
|
|
assert.equal(annotationSpy.lastCall.args[0], el);
|
|
assert.equal(annotationSpy.lastCall.args[1], start);
|
|
assert.equal(annotationSpy.lastCall.args[2], length);
|
|
assert.equal(annotationSpy.lastCall.args[3], className);
|
|
assert.isOk(el.querySelector('hl.' + className));
|
|
});
|
|
|
|
test('annotate with range but disabled does nothing', function() {
|
|
var str = 'Etiam dui, blandit wisi.';
|
|
var start = 6;
|
|
var length = 3;
|
|
var className = 'foobar';
|
|
|
|
var annotationSpy = sandbox.spy(GrAnnotation, 'annotateElement');
|
|
var el = document.createElement('div');
|
|
el.textContent = str;
|
|
var line = new GrDiffLine(GrDiffLine.Type.REMOVE);
|
|
line.beforeNumber = 12;
|
|
element._baseRanges[11] = [{
|
|
start: start,
|
|
length: length,
|
|
className: className,
|
|
}];
|
|
element.enabled = false;
|
|
|
|
element.annotate(el, line);
|
|
|
|
assert.isFalse(annotationSpy.called);
|
|
});
|
|
|
|
test('process on empty diff does nothing', function(done) {
|
|
element.diff = {
|
|
meta_a: {content_type: 'application/json'},
|
|
meta_b: {content_type: 'application/json'},
|
|
content: [],
|
|
};
|
|
var processNextSpy = sandbox.spy(element, '_processNextLine');
|
|
|
|
var processPromise = element.process();
|
|
|
|
processPromise.then(function() {
|
|
assert.isFalse(processNextSpy.called);
|
|
assert.equal(element._baseRanges.length, 0);
|
|
assert.equal(element._revisionRanges.length, 0);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('process for unsupported languages does nothing', function(done) {
|
|
element.diff = {
|
|
meta_a: {content_type: 'text/x+objective-cobol-plus-plus'},
|
|
meta_b: {content_type: 'application/not-a-real-language'},
|
|
content: [],
|
|
};
|
|
var processNextSpy = sandbox.spy(element, '_processNextLine');
|
|
|
|
var processPromise = element.process();
|
|
|
|
processPromise.then(function() {
|
|
assert.isFalse(processNextSpy.called);
|
|
assert.equal(element._baseRanges.length, 0);
|
|
assert.equal(element._revisionRanges.length, 0);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('process while disabled does nothing', function(done) {
|
|
var processNextSpy = sandbox.spy(element, '_processNextLine');
|
|
element.enabled = false;
|
|
var loadHLJSSpy = sandbox.spy(element, '_loadHLJS');
|
|
|
|
var processPromise = element.process();
|
|
|
|
processPromise.then(function() {
|
|
assert.isFalse(processNextSpy.called);
|
|
assert.equal(element._baseRanges.length, 0);
|
|
assert.equal(element._revisionRanges.length, 0);
|
|
assert.isFalse(loadHLJSSpy.called);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('process highlight ipsum', function(done) {
|
|
element.diff.meta_a.content_type = 'application/json';
|
|
element.diff.meta_b.content_type = 'application/json';
|
|
|
|
var mockHLJS = getMockHLJS();
|
|
var highlightSpy = sinon.spy(mockHLJS, 'highlight');
|
|
sandbox.stub(element.$.libLoader, 'get',
|
|
function() { return Promise.resolve(mockHLJS); });
|
|
var processNextSpy = sandbox.spy(element, '_processNextLine');
|
|
var processPromise = element.process();
|
|
|
|
processPromise.then(function() {
|
|
var linesA = diff.meta_a.lines;
|
|
var linesB = diff.meta_b.lines;
|
|
|
|
assert.isTrue(processNextSpy.called);
|
|
assert.equal(element._baseRanges.length, linesA);
|
|
assert.equal(element._revisionRanges.length, linesB);
|
|
|
|
assert.equal(highlightSpy.callCount, linesA + linesB);
|
|
|
|
// The first line of both sides have a range.
|
|
[element._baseRanges[0], element._revisionRanges[0]]
|
|
.forEach(function(range) {
|
|
assert.equal(range.length, 1);
|
|
assert.equal(range[0].className,
|
|
'gr-diff gr-syntax gr-syntax-string');
|
|
assert.equal(range[0].start, 'lorem '.length);
|
|
assert.equal(range[0].length, 'ipsum'.length);
|
|
});
|
|
|
|
// There are no ranges from ll.1-12 on the left and ll.1-11 on the
|
|
// right.
|
|
element._baseRanges.slice(1, 12)
|
|
.concat(element._revisionRanges.slice(1, 11))
|
|
.forEach(function(range) {
|
|
assert.equal(range.length, 0);
|
|
});
|
|
|
|
// There should be another pair of ranges on l.13 for the left and
|
|
// l.12 for the right.
|
|
[element._baseRanges[13], element._revisionRanges[12]]
|
|
.forEach(function(range) {
|
|
assert.equal(range.length, 1);
|
|
assert.equal(range[0].className,
|
|
'gr-diff gr-syntax gr-syntax-string');
|
|
assert.equal(range[0].start, 32);
|
|
assert.equal(range[0].length, 'ipsum'.length);
|
|
});
|
|
|
|
// The next group should have a similar instance on either side.
|
|
|
|
var range = element._baseRanges[15];
|
|
assert.equal(range.length, 1);
|
|
assert.equal(range[0].className, 'gr-diff gr-syntax gr-syntax-string');
|
|
assert.equal(range[0].start, 34);
|
|
assert.equal(range[0].length, 'ipsum'.length);
|
|
|
|
range = element._revisionRanges[14];
|
|
assert.equal(range.length, 1);
|
|
assert.equal(range[0].className, 'gr-diff gr-syntax gr-syntax-string');
|
|
assert.equal(range[0].start, 35);
|
|
assert.equal(range[0].length, 'ipsum'.length);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('_diffChanged calls cancel', function() {
|
|
var cancelSpy = sandbox.spy(element, '_diffChanged');
|
|
element.diff = {content: []};
|
|
assert.isTrue(cancelSpy.called);
|
|
});
|
|
|
|
test('_rangesFromElement no ranges', function() {
|
|
var elem = document.createElement('span');
|
|
elem.textContent = 'Etiam dui, blandit wisi.';
|
|
var offset = 100;
|
|
|
|
var result = element._rangesFromElement(elem, offset);
|
|
|
|
assert.equal(result.length, 0);
|
|
});
|
|
|
|
test('_rangesFromElement single range', function() {
|
|
var str0 = 'Etiam ';
|
|
var str1 = 'dui, blandit';
|
|
var str2 = ' wisi.';
|
|
var className = 'gr-diff gr-syntax gr-syntax-string';
|
|
var offset = 100;
|
|
|
|
var elem = document.createElement('span');
|
|
elem.appendChild(document.createTextNode(str0));
|
|
var span = document.createElement('span');
|
|
span.textContent = str1;
|
|
span.className = className;
|
|
elem.appendChild(span);
|
|
elem.appendChild(document.createTextNode(str2));
|
|
|
|
var result = element._rangesFromElement(elem, offset);
|
|
|
|
assert.equal(result.length, 1);
|
|
assert.equal(result[0].start, str0.length + offset);
|
|
assert.equal(result[0].length, str1.length);
|
|
assert.equal(result[0].className, className);
|
|
});
|
|
|
|
test('_rangesFromElement non-whitelist', function() {
|
|
var str0 = 'Etiam ';
|
|
var str1 = 'dui, blandit';
|
|
var str2 = ' wisi.';
|
|
var className = 'not-in-the-whitelist';
|
|
var offset = 100;
|
|
|
|
var elem = document.createElement('span');
|
|
elem.appendChild(document.createTextNode(str0));
|
|
var span = document.createElement('span');
|
|
span.textContent = str1;
|
|
span.className = className;
|
|
elem.appendChild(span);
|
|
elem.appendChild(document.createTextNode(str2));
|
|
|
|
var result = element._rangesFromElement(elem, offset);
|
|
|
|
assert.equal(result.length, 0);
|
|
});
|
|
|
|
test('_rangesFromElement milti range', function() {
|
|
var str0 = 'Etiam ';
|
|
var str1 = 'dui,';
|
|
var str2 = ' blandit';
|
|
var str3 = ' wisi.';
|
|
var className = 'gr-diff gr-syntax gr-syntax-string';
|
|
var offset = 100;
|
|
|
|
var elem = document.createElement('span');
|
|
elem.appendChild(document.createTextNode(str0));
|
|
var span = document.createElement('span');
|
|
span.textContent = str1;
|
|
span.className = className;
|
|
elem.appendChild(span);
|
|
elem.appendChild(document.createTextNode(str2));
|
|
span = document.createElement('span');
|
|
span.textContent = str3;
|
|
span.className = className;
|
|
elem.appendChild(span);
|
|
|
|
var result = element._rangesFromElement(elem, offset);
|
|
|
|
assert.equal(result.length, 2);
|
|
|
|
assert.equal(result[0].start, str0.length + offset);
|
|
assert.equal(result[0].length, str1.length);
|
|
assert.equal(result[0].className, className);
|
|
|
|
assert.equal(result[1].start,
|
|
str0.length + str1.length + str2.length + offset);
|
|
assert.equal(result[1].length, str3.length);
|
|
assert.equal(result[1].className, className);
|
|
});
|
|
|
|
test('_rangesFromElement nested range', function() {
|
|
var str0 = 'Etiam ';
|
|
var str1 = 'dui,';
|
|
var str2 = ' blandit';
|
|
var str3 = ' wisi.';
|
|
var className = 'gr-diff gr-syntax gr-syntax-string';
|
|
var offset = 100;
|
|
|
|
var elem = document.createElement('span');
|
|
elem.appendChild(document.createTextNode(str0));
|
|
var span1 = document.createElement('span');
|
|
span1.textContent = str1;
|
|
span1.className = className;
|
|
elem.appendChild(span1);
|
|
var span2 = document.createElement('span');
|
|
span2.textContent = str2;
|
|
span2.className = className;
|
|
span1.appendChild(span2);
|
|
elem.appendChild(document.createTextNode(str3));
|
|
|
|
var result = element._rangesFromElement(elem, offset);
|
|
|
|
assert.equal(result.length, 2);
|
|
|
|
assert.equal(result[0].start, str0.length + offset);
|
|
assert.equal(result[0].length, str1.length + str2.length);
|
|
assert.equal(result[0].className, className);
|
|
|
|
assert.equal(result[1].start, str0.length + str1.length + offset);
|
|
assert.equal(result[1].length, str2.length);
|
|
assert.equal(result[1].className, className);
|
|
});
|
|
|
|
test('_rangesFromString whitelist allows recursion', function() {
|
|
var str = [
|
|
'<span class="non-whtelisted-class">',
|
|
'<span class="gr-diff gr-syntax gr-syntax-keyword">public</span>',
|
|
'</span>'].join('');
|
|
var result = element._rangesFromString(str);
|
|
assert.notEqual(result.length, 0);
|
|
});
|
|
|
|
test('_isSectionDone', function() {
|
|
var state = {sectionIndex: 0, lineIndex: 0};
|
|
assert.isFalse(element._isSectionDone(state));
|
|
|
|
state = {sectionIndex: 0, lineIndex: 2};
|
|
assert.isFalse(element._isSectionDone(state));
|
|
|
|
state = {sectionIndex: 0, lineIndex: 4};
|
|
assert.isTrue(element._isSectionDone(state));
|
|
|
|
state = {sectionIndex: 1, lineIndex: 2};
|
|
assert.isFalse(element._isSectionDone(state));
|
|
|
|
state = {sectionIndex: 1, lineIndex: 3};
|
|
assert.isTrue(element._isSectionDone(state));
|
|
|
|
state = {sectionIndex: 3, lineIndex: 0};
|
|
assert.isFalse(element._isSectionDone(state));
|
|
|
|
state = {sectionIndex: 3, lineIndex: 3};
|
|
assert.isFalse(element._isSectionDone(state));
|
|
|
|
state = {sectionIndex: 3, lineIndex: 4};
|
|
assert.isTrue(element._isSectionDone(state));
|
|
});
|
|
|
|
test('workaround CPP LT directive', function() {
|
|
// Does nothing to regular line.
|
|
var line = 'int main(int argc, char** argv) { return 0; }';
|
|
assert.equal(element._workaround('cpp', line), line);
|
|
|
|
// Does nothing to include directive.
|
|
line = '#include <stdio>';
|
|
assert.equal(element._workaround('cpp', line), line);
|
|
|
|
// Converts left-shift operator in #define.
|
|
line = '#define GiB (1ull << 30)';
|
|
var expected = '#define GiB (1ull || 30)';
|
|
assert.equal(element._workaround('cpp', line), expected);
|
|
|
|
// Converts less-than operator in #if.
|
|
line = ' #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1)';
|
|
expected = ' #if __GNUC__ | 4 || (__GNUC__ == 4 && __GNUC_MINOR__ | 1)';
|
|
assert.equal(element._workaround('cpp', line), expected);
|
|
});
|
|
|
|
test('workaround Java param-annotation', function() {
|
|
// Does nothing to regular line.
|
|
var line = 'public static void foo(int bar) { }';
|
|
assert.equal(element._workaround('java', line), line);
|
|
|
|
// Does nothing to regular annotation.
|
|
var line = 'public static void foo(@Nullable int bar) { }';
|
|
assert.equal(element._workaround('java', line), line);
|
|
|
|
// Converts parameterized annotation.
|
|
line = 'public static void foo(@SuppressWarnings("unused") int bar) { }';
|
|
var expected = 'public static void foo(@SuppressWarnings "unused" ' +
|
|
' int bar) { }';
|
|
assert.equal(element._workaround('java', line), expected);
|
|
});
|
|
});
|
|
</script>
|