
Follow-up to https://gerrit-review.googlesource.com/c/gerrit/+/157590 which no longer used locationRange to group comments together into threads. However, locationRange was still used for a few other things including thread removal, which could result in a bug if there were two threads that had the same locationRange and the wrong one was found first. Things included in this change: 1. Determine whether a thread group exists - This is identified as it was before. The thread group now gets a commentSide attribute, as it can pass it to all of its children (the same for all). 2. Determine if a thread exists within the group for a given range in a group - The range object is now included as an attribute on the comment thread instead of the locationRange string. 3. Be able to remove a thread group - A rootId attribute is now stored on the comment thread, which is data bound to each comment. When a comment is removed, it finds the thread with the rootId within the thread group to remove. - In the event that a thread group only contains a single draft, there is no roottId. Change-Id: I4f7804ac02f4259b4964c6333d258f0fc3b29d24
1116 lines
39 KiB
HTML
1116 lines
39 KiB
HTML
<!DOCTYPE html>
|
||
<!--
|
||
Copyright (C) 2015 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</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"/>
|
||
<script src="../../../scripts/util.js"></script>
|
||
|
||
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
|
||
<link rel="import" href="gr-diff.html">
|
||
|
||
<script>void(0);</script>
|
||
|
||
<test-fixture id="basic">
|
||
<template>
|
||
<gr-diff></gr-diff>
|
||
</template>
|
||
</test-fixture>
|
||
|
||
<script>
|
||
suite('gr-diff tests', () => {
|
||
let element;
|
||
let sandbox;
|
||
|
||
setup(() => {
|
||
sandbox = sinon.sandbox.create();
|
||
});
|
||
|
||
teardown(() => {
|
||
sandbox.restore();
|
||
});
|
||
|
||
test('reload cancels before network resolves', () => {
|
||
element = fixture('basic');
|
||
const cancelStub = sandbox.stub(element.$.diffBuilder, 'cancel');
|
||
|
||
// Stub the network calls into requests that never resolve.
|
||
sandbox.stub(element, '_getDiff', () => new Promise(() => {}));
|
||
|
||
element.reload();
|
||
assert.isTrue(cancelStub.called);
|
||
});
|
||
|
||
test('_diffLength', () => {
|
||
element = fixture('basic');
|
||
const mock = document.createElement('mock-diff-response');
|
||
assert.equal(element._diffLength(mock.diffResponse), 52);
|
||
});
|
||
|
||
|
||
suite('_get{PatchNum|IsParentComment}ByLineAndContent', () => {
|
||
let lineEl;
|
||
let contentEl;
|
||
|
||
setup(() => {
|
||
element = fixture('basic');
|
||
lineEl = document.createElement('td');
|
||
contentEl = document.createElement('span');
|
||
});
|
||
|
||
suite('_getPatchNumByLineAndContent', () => {
|
||
test('right side', () => {
|
||
element.patchRange = {patchNum: 4, basePatchNum: 'PARENT'};
|
||
lineEl.classList.add('right');
|
||
assert.equal(element._getPatchNumByLineAndContent(lineEl, contentEl),
|
||
4);
|
||
});
|
||
|
||
test('left side parent by linenum', () => {
|
||
element.patchRange = {patchNum: 4, basePatchNum: 'PARENT'};
|
||
lineEl.classList.add('left');
|
||
assert.equal(element._getPatchNumByLineAndContent(lineEl, contentEl),
|
||
4);
|
||
});
|
||
|
||
test('left side parent by content', () => {
|
||
element.patchRange = {patchNum: 4, basePatchNum: 'PARENT'};
|
||
contentEl.classList.add('remove');
|
||
assert.equal(element._getPatchNumByLineAndContent(lineEl, contentEl),
|
||
4);
|
||
});
|
||
|
||
test('left side merge parent', () => {
|
||
element.patchRange = {patchNum: 4, basePatchNum: -2};
|
||
contentEl.classList.add('remove');
|
||
assert.equal(element._getPatchNumByLineAndContent(lineEl, contentEl),
|
||
4);
|
||
});
|
||
|
||
test('left side non parent', () => {
|
||
element.patchRange = {patchNum: 4, basePatchNum: 3};
|
||
contentEl.classList.add('remove');
|
||
assert.equal(element._getPatchNumByLineAndContent(lineEl, contentEl),
|
||
3);
|
||
});
|
||
});
|
||
|
||
suite('_getIsParentCommentByLineAndContent', () => {
|
||
test('right side', () => {
|
||
element.patchRange = {patchNum: 4, basePatchNum: 'PARENT'};
|
||
lineEl.classList.add('right');
|
||
assert.isFalse(
|
||
element._getIsParentCommentByLineAndContent(lineEl, contentEl));
|
||
});
|
||
|
||
test('left side parent by linenum', () => {
|
||
element.patchRange = {patchNum: 4, basePatchNum: 'PARENT'};
|
||
lineEl.classList.add('left');
|
||
assert.isTrue(
|
||
element._getIsParentCommentByLineAndContent(lineEl, contentEl));
|
||
});
|
||
|
||
test('left side parent by content', () => {
|
||
element.patchRange = {patchNum: 4, basePatchNum: 'PARENT'};
|
||
contentEl.classList.add('remove');
|
||
assert.isTrue(
|
||
element._getIsParentCommentByLineAndContent(lineEl, contentEl));
|
||
});
|
||
|
||
test('left side merge parent', () => {
|
||
element.patchRange = {patchNum: 4, basePatchNum: -2};
|
||
contentEl.classList.add('remove');
|
||
assert.isTrue(
|
||
element._getIsParentCommentByLineAndContent(lineEl, contentEl));
|
||
});
|
||
|
||
test('left side non parent', () => {
|
||
element.patchRange = {patchNum: 4, basePatchNum: 3};
|
||
contentEl.classList.add('remove');
|
||
assert.isFalse(
|
||
element._getIsParentCommentByLineAndContent(lineEl, contentEl));
|
||
});
|
||
});
|
||
});
|
||
|
||
suite('not logged in', () => {
|
||
setup(() => {
|
||
stub('gr-rest-api-interface', {
|
||
getLoggedIn() { return Promise.resolve(false); },
|
||
});
|
||
element = fixture('basic');
|
||
});
|
||
|
||
test('toggleLeftDiff', () => {
|
||
element.toggleLeftDiff();
|
||
assert.isTrue(element.classList.contains('no-left'));
|
||
element.toggleLeftDiff();
|
||
assert.isFalse(element.classList.contains('no-left'));
|
||
});
|
||
|
||
test('addDraftAtLine', done => {
|
||
sandbox.stub(element, '_selectLine');
|
||
const loggedInErrorSpy = sandbox.spy();
|
||
element.addEventListener('show-auth-required', loggedInErrorSpy);
|
||
element.addDraftAtLine();
|
||
flush(() => {
|
||
assert.isTrue(loggedInErrorSpy.called);
|
||
done();
|
||
});
|
||
});
|
||
|
||
test('view does not start with displayLine classList', () => {
|
||
assert.isFalse(
|
||
element.$$('.diffContainer').classList.contains('displayLine'));
|
||
});
|
||
|
||
test('displayLine class added called when displayLine is true', () => {
|
||
const spy = sandbox.spy(element, '_computeContainerClass');
|
||
element.displayLine = true;
|
||
assert.isTrue(spy.called);
|
||
assert.isTrue(
|
||
element.$$('.diffContainer').classList.contains('displayLine'));
|
||
});
|
||
|
||
test('loads files weblinks', () => {
|
||
const weblinksStub = sandbox.stub(Gerrit.Nav, '_generateWeblinks')
|
||
.returns({name: 'stubb', url: '#s'});
|
||
sandbox.stub(element.$.restAPI, 'getDiff').returns(Promise.resolve({}));
|
||
element.projectName = 'test-project';
|
||
element.path = 'test-path';
|
||
element.commitRange = {baseCommit: 'test-base', commit: 'test-commit'};
|
||
element.patchRange = {};
|
||
return element._getDiff().then(() => {
|
||
assert.isTrue(weblinksStub.calledTwice);
|
||
assert.isTrue(weblinksStub.firstCall.calledWith({
|
||
commit: 'test-base',
|
||
file: 'test-path',
|
||
options: {
|
||
weblinks: undefined,
|
||
},
|
||
repo: 'test-project',
|
||
type: Gerrit.Nav.WeblinkType.FILE}));
|
||
assert.isTrue(weblinksStub.secondCall.calledWith({
|
||
commit: 'test-commit',
|
||
file: 'test-path',
|
||
options: {
|
||
weblinks: undefined,
|
||
},
|
||
repo: 'test-project',
|
||
type: Gerrit.Nav.WeblinkType.FILE}));
|
||
assert.deepEqual(element.filesWeblinks, {
|
||
meta_a: [{name: 'stubb', url: '#s'}],
|
||
meta_b: [{name: 'stubb', url: '#s'}],
|
||
});
|
||
});
|
||
});
|
||
|
||
test('remove comment', () => {
|
||
element.comments = {
|
||
meta: {
|
||
changeNum: '42',
|
||
patchRange: {
|
||
basePatchNum: 'PARENT',
|
||
patchNum: 3,
|
||
},
|
||
path: '/path/to/foo',
|
||
projectConfig: {foo: 'bar'},
|
||
},
|
||
left: [
|
||
{id: 'bc1', side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bc2', side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
],
|
||
right: [
|
||
{id: 'c1', __commentSide: 'right'},
|
||
{id: 'c2', __commentSide: 'right'},
|
||
{id: 'd1', __draft: true, __commentSide: 'right'},
|
||
{id: 'd2', __draft: true, __commentSide: 'right'},
|
||
],
|
||
};
|
||
|
||
element._removeComment({});
|
||
// Using JSON.stringify because Safari 9.1 (11601.5.17.1) doesn’t seem
|
||
// to believe that one object deepEquals another even when they do :-/.
|
||
assert.equal(JSON.stringify(element.comments), JSON.stringify({
|
||
meta: {
|
||
changeNum: '42',
|
||
patchRange: {
|
||
basePatchNum: 'PARENT',
|
||
patchNum: 3,
|
||
},
|
||
path: '/path/to/foo',
|
||
projectConfig: {foo: 'bar'},
|
||
},
|
||
left: [
|
||
{id: 'bc1', side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bc2', side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
],
|
||
right: [
|
||
{id: 'c1', __commentSide: 'right'},
|
||
{id: 'c2', __commentSide: 'right'},
|
||
{id: 'd1', __draft: true, __commentSide: 'right'},
|
||
{id: 'd2', __draft: true, __commentSide: 'right'},
|
||
],
|
||
}));
|
||
|
||
element._removeComment({id: 'bc2', side: 'PARENT',
|
||
__commentSide: 'left'});
|
||
assert.deepEqual(element.comments, {
|
||
meta: {
|
||
changeNum: '42',
|
||
patchRange: {
|
||
basePatchNum: 'PARENT',
|
||
patchNum: 3,
|
||
},
|
||
path: '/path/to/foo',
|
||
projectConfig: {foo: 'bar'},
|
||
},
|
||
left: [
|
||
{id: 'bc1', side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
],
|
||
right: [
|
||
{id: 'c1', __commentSide: 'right'},
|
||
{id: 'c2', __commentSide: 'right'},
|
||
{id: 'd1', __draft: true, __commentSide: 'right'},
|
||
{id: 'd2', __draft: true, __commentSide: 'right'},
|
||
],
|
||
});
|
||
|
||
element._removeComment({id: 'd2', __commentSide: 'right'});
|
||
assert.deepEqual(element.comments, {
|
||
meta: {
|
||
changeNum: '42',
|
||
patchRange: {
|
||
basePatchNum: 'PARENT',
|
||
patchNum: 3,
|
||
},
|
||
path: '/path/to/foo',
|
||
projectConfig: {foo: 'bar'},
|
||
},
|
||
left: [
|
||
{id: 'bc1', side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
],
|
||
right: [
|
||
{id: 'c1', __commentSide: 'right'},
|
||
{id: 'c2', __commentSide: 'right'},
|
||
{id: 'd1', __draft: true, __commentSide: 'right'},
|
||
],
|
||
});
|
||
});
|
||
|
||
test('_getRangeString', () => {
|
||
const side = 'PARENT';
|
||
const range = {
|
||
startLine: 1,
|
||
startChar: 1,
|
||
endLine: 1,
|
||
endChar: 2,
|
||
};
|
||
assert.equal(element._getRangeString(side, range),
|
||
'range-1-1-1-2-PARENT');
|
||
assert.equal(element._getRangeString(side, null),
|
||
'line-PARENT');
|
||
}),
|
||
|
||
test('thread groups', () => {
|
||
const contentEl = document.createElement('div');
|
||
const commentSide = 'left';
|
||
const patchNum = 1;
|
||
const side = 'PARENT';
|
||
let range = {
|
||
startLine: 1,
|
||
startChar: 1,
|
||
endLine: 1,
|
||
endChar: 2,
|
||
};
|
||
|
||
element.changeNum = 123;
|
||
element.patchRange = {basePatchNum: 1, patchNum: 2};
|
||
element.path = 'file.txt';
|
||
|
||
const mock = document.createElement('mock-diff-response');
|
||
element.$.diffBuilder._builder = element.$.diffBuilder._getDiffBuilder(
|
||
mock.diffResponse, {}, {tab_size: 2, line_length: 80});
|
||
|
||
// No thread groups.
|
||
assert.isNotOk(element._getThreadGroupForLine(contentEl));
|
||
|
||
// A thread group gets created.
|
||
assert.isOk(element._getOrCreateThread(contentEl,
|
||
patchNum, commentSide, side));
|
||
|
||
// Try to fetch a thread with a different range.
|
||
range = {
|
||
startLine: 1,
|
||
startChar: 1,
|
||
endLine: 1,
|
||
endChar: 3,
|
||
};
|
||
|
||
assert.isOk(element._getOrCreateThread(
|
||
contentEl, patchNum, commentSide, side, range));
|
||
// The new thread group can be fetched.
|
||
assert.isOk(element._getThreadGroupForLine(contentEl));
|
||
|
||
assert.equal(contentEl.querySelectorAll(
|
||
'gr-diff-comment-thread-group').length, 1);
|
||
|
||
const threadGroup = contentEl.querySelector(
|
||
'gr-diff-comment-thread-group');
|
||
const threadLength = Polymer.dom(threadGroup.root).
|
||
querySelectorAll('gr-diff-comment-thread').length;
|
||
assert.equal(threadLength, 2);
|
||
});
|
||
|
||
suite('image diffs', () => {
|
||
let mockFile1;
|
||
let mockFile2;
|
||
const stubs = [];
|
||
setup(() => {
|
||
mockFile1 = {
|
||
body: 'Qk06AAAAAAAAADYAAAAoAAAAAQAAAP////8BACAAAAAAAAAAAAATCwAAE' +
|
||
'wsAAAAAAAAAAAAAAAAA/w==',
|
||
type: 'image/bmp',
|
||
};
|
||
mockFile2 = {
|
||
body: 'Qk06AAAAAAAAADYAAAAoAAAAAQAAAP////8BACAAAAAAAAAAAAATCwAAE' +
|
||
'wsAAAAAAAAAAAAA/////w==',
|
||
type: 'image/bmp',
|
||
};
|
||
const mockCommit = {
|
||
commit: '9a1a1d10baece5efbba10bc4ccf808a67a50ac0a',
|
||
parents: [{
|
||
commit: '7338aa9adfe57909f1fdaf88975cdea467d3382f',
|
||
subject: 'Added a carrot',
|
||
}],
|
||
author: {
|
||
name: 'Wyatt Allen',
|
||
email: 'wyatta@google.com',
|
||
date: '2016-05-23 21:44:51.000000000',
|
||
tz: -420,
|
||
},
|
||
committer: {
|
||
name: 'Wyatt Allen',
|
||
email: 'wyatta@google.com',
|
||
date: '2016-05-25 00:25:41.000000000',
|
||
tz: -420,
|
||
},
|
||
subject: 'Updated the carrot',
|
||
message: 'Updated the carrot\n\nChange-Id: Iabcd123\n',
|
||
};
|
||
const mockComments = {baseComments: [], comments: []};
|
||
|
||
stubs.push(sandbox.stub(element.$.restAPI, 'getCommitInfo',
|
||
() => Promise.resolve(mockCommit)));
|
||
stubs.push(sandbox.stub(element.$.restAPI,
|
||
'getB64FileContents',
|
||
(changeId, patchNum, path, opt_parentIndex) => {
|
||
return Promise.resolve(opt_parentIndex === 1 ? mockFile1 :
|
||
mockFile2);
|
||
}));
|
||
stubs.push(sandbox.stub(element.$.restAPI, '_getDiffComments',
|
||
() => Promise.resolve(mockComments)));
|
||
stubs.push(sandbox.stub(element.$.restAPI, 'getDiffDrafts',
|
||
() => Promise.resolve(mockComments)));
|
||
|
||
element.patchRange = {basePatchNum: 'PARENT', patchNum: 1};
|
||
element.comments = {left: [], right: []};
|
||
});
|
||
|
||
test('renders image diffs with same file name', done => {
|
||
const mockDiff = {
|
||
meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 66},
|
||
meta_b: {name: 'carrot.jpg', content_type: 'image/jpeg',
|
||
lines: 560},
|
||
intraline_status: 'OK',
|
||
change_type: 'MODIFIED',
|
||
diff_header: [
|
||
'diff --git a/carrot.jpg b/carrot.jpg',
|
||
'index 2adc47d..f9c2f2c 100644',
|
||
'--- a/carrot.jpg',
|
||
'+++ b/carrot.jpg',
|
||
'Binary files differ',
|
||
],
|
||
content: [{skip: 66}],
|
||
binary: true,
|
||
};
|
||
stubs.push(sandbox.stub(element, '_getDiff',
|
||
() => Promise.resolve(mockDiff)));
|
||
|
||
const rendered = () => {
|
||
// Recognizes that it should be an image diff.
|
||
assert.isTrue(element.isImageDiff);
|
||
assert.instanceOf(
|
||
element.$.diffBuilder._builder, GrDiffBuilderImage);
|
||
|
||
// Left image rendered with the parent commit's version of the file.
|
||
const leftImage = element.$.diffTable.querySelector('td.left img');
|
||
const leftLabel =
|
||
element.$.diffTable.querySelector('td.left label');
|
||
const leftLabelContent = leftLabel.querySelector('.label');
|
||
const leftLabelName = leftLabel.querySelector('.name');
|
||
|
||
const rightImage =
|
||
element.$.diffTable.querySelector('td.right img');
|
||
const rightLabel = element.$.diffTable.querySelector(
|
||
'td.right label');
|
||
const rightLabelContent = rightLabel.querySelector('.label');
|
||
const rightLabelName = rightLabel.querySelector('.name');
|
||
|
||
assert.isNotOk(rightLabelName);
|
||
assert.isNotOk(leftLabelName);
|
||
|
||
let leftLoaded = false;
|
||
let rightLoaded = false;
|
||
|
||
leftImage.addEventListener('load', () => {
|
||
assert.isOk(leftImage);
|
||
assert.equal(leftImage.getAttribute('src'),
|
||
'data:image/bmp;base64, ' + mockFile1.body);
|
||
assert.equal(leftLabelContent.textContent, '1×1 image/bmp');
|
||
leftLoaded = true;
|
||
if (rightLoaded) {
|
||
element.removeEventListener('render', rendered);
|
||
done();
|
||
}
|
||
});
|
||
|
||
rightImage.addEventListener('load', () => {
|
||
assert.isOk(rightImage);
|
||
assert.equal(rightImage.getAttribute('src'),
|
||
'data:image/bmp;base64, ' + mockFile2.body);
|
||
assert.equal(rightLabelContent.textContent, '1×1 image/bmp');
|
||
|
||
rightLoaded = true;
|
||
if (leftLoaded) {
|
||
element.removeEventListener('render', rendered);
|
||
done();
|
||
}
|
||
});
|
||
};
|
||
|
||
element.addEventListener('render', rendered);
|
||
|
||
element.$.restAPI.getDiffPreferences().then(prefs => {
|
||
element.prefs = prefs;
|
||
element.reload();
|
||
});
|
||
});
|
||
|
||
test('renders image diffs with a different file name', done => {
|
||
const mockDiff = {
|
||
meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 66},
|
||
meta_b: {name: 'carrot2.jpg', content_type: 'image/jpeg',
|
||
lines: 560},
|
||
intraline_status: 'OK',
|
||
change_type: 'MODIFIED',
|
||
diff_header: [
|
||
'diff --git a/carrot.jpg b/carrot2.jpg',
|
||
'index 2adc47d..f9c2f2c 100644',
|
||
'--- a/carrot.jpg',
|
||
'+++ b/carrot2.jpg',
|
||
'Binary files differ',
|
||
],
|
||
content: [{skip: 66}],
|
||
binary: true,
|
||
};
|
||
stubs.push(sandbox.stub(element, '_getDiff',
|
||
() => Promise.resolve(mockDiff)));
|
||
|
||
const rendered = () => {
|
||
// Recognizes that it should be an image diff.
|
||
assert.isTrue(element.isImageDiff);
|
||
assert.instanceOf(
|
||
element.$.diffBuilder._builder, GrDiffBuilderImage);
|
||
|
||
// Left image rendered with the parent commit's version of the file.
|
||
const leftImage = element.$.diffTable.querySelector('td.left img');
|
||
const leftLabel =
|
||
element.$.diffTable.querySelector('td.left label');
|
||
const leftLabelContent = leftLabel.querySelector('.label');
|
||
const leftLabelName = leftLabel.querySelector('.name');
|
||
|
||
const rightImage =
|
||
element.$.diffTable.querySelector('td.right img');
|
||
const rightLabel = element.$.diffTable.querySelector(
|
||
'td.right label');
|
||
const rightLabelContent = rightLabel.querySelector('.label');
|
||
const rightLabelName = rightLabel.querySelector('.name');
|
||
|
||
assert.isOk(rightLabelName);
|
||
assert.isOk(leftLabelName);
|
||
assert.equal(leftLabelName.textContent, mockDiff.meta_a.name);
|
||
assert.equal(rightLabelName.textContent, mockDiff.meta_b.name);
|
||
|
||
let leftLoaded = false;
|
||
let rightLoaded = false;
|
||
|
||
leftImage.addEventListener('load', () => {
|
||
assert.isOk(leftImage);
|
||
assert.equal(leftImage.getAttribute('src'),
|
||
'data:image/bmp;base64, ' + mockFile1.body);
|
||
assert.equal(leftLabelContent.textContent, '1×1 image/bmp');
|
||
leftLoaded = true;
|
||
if (rightLoaded) {
|
||
element.removeEventListener('render', rendered);
|
||
done();
|
||
}
|
||
});
|
||
|
||
rightImage.addEventListener('load', () => {
|
||
assert.isOk(rightImage);
|
||
assert.equal(rightImage.getAttribute('src'),
|
||
'data:image/bmp;base64, ' + mockFile2.body);
|
||
assert.equal(rightLabelContent.textContent, '1×1 image/bmp');
|
||
|
||
rightLoaded = true;
|
||
if (leftLoaded) {
|
||
element.removeEventListener('render', rendered);
|
||
done();
|
||
}
|
||
});
|
||
};
|
||
|
||
element.addEventListener('render', rendered);
|
||
|
||
element.$.restAPI.getDiffPreferences().then(prefs => {
|
||
element.prefs = prefs;
|
||
element.reload();
|
||
});
|
||
});
|
||
|
||
test('renders added image', done => {
|
||
const mockDiff = {
|
||
meta_b: {name: 'carrot.jpg', content_type: 'image/jpeg',
|
||
lines: 560},
|
||
intraline_status: 'OK',
|
||
change_type: 'ADDED',
|
||
diff_header: [
|
||
'diff --git a/carrot.jpg b/carrot.jpg',
|
||
'index 0000000..f9c2f2c 100644',
|
||
'--- /dev/null',
|
||
'+++ b/carrot.jpg',
|
||
'Binary files differ',
|
||
],
|
||
content: [{skip: 66}],
|
||
binary: true,
|
||
};
|
||
stubs.push(sandbox.stub(element, '_getDiff',
|
||
() => Promise.resolve(mockDiff)));
|
||
|
||
element.addEventListener('render', () => {
|
||
// Recognizes that it should be an image diff.
|
||
assert.isTrue(element.isImageDiff);
|
||
assert.instanceOf(
|
||
element.$.diffBuilder._builder, GrDiffBuilderImage);
|
||
|
||
const leftImage = element.$.diffTable.querySelector('td.left img');
|
||
const rightImage = element.$.diffTable.querySelector('td.right img');
|
||
|
||
assert.isNotOk(leftImage);
|
||
assert.isOk(rightImage);
|
||
done();
|
||
});
|
||
|
||
element.$.restAPI.getDiffPreferences().then(prefs => {
|
||
element.prefs = prefs;
|
||
element.reload();
|
||
});
|
||
});
|
||
|
||
test('renders removed image', done => {
|
||
const mockDiff = {
|
||
meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg',
|
||
lines: 560},
|
||
intraline_status: 'OK',
|
||
change_type: 'DELETED',
|
||
diff_header: [
|
||
'diff --git a/carrot.jpg b/carrot.jpg',
|
||
'index f9c2f2c..0000000 100644',
|
||
'--- a/carrot.jpg',
|
||
'+++ /dev/null',
|
||
'Binary files differ',
|
||
],
|
||
content: [{skip: 66}],
|
||
binary: true,
|
||
};
|
||
stubs.push(sandbox.stub(element, '_getDiff',
|
||
() => Promise.resolve(mockDiff)));
|
||
|
||
element.addEventListener('render', () => {
|
||
// Recognizes that it should be an image diff.
|
||
assert.isTrue(element.isImageDiff);
|
||
assert.instanceOf(
|
||
element.$.diffBuilder._builder, GrDiffBuilderImage);
|
||
|
||
const leftImage = element.$.diffTable.querySelector('td.left img');
|
||
const rightImage = element.$.diffTable.querySelector('td.right img');
|
||
|
||
assert.isOk(leftImage);
|
||
assert.isNotOk(rightImage);
|
||
done();
|
||
});
|
||
|
||
element.$.restAPI.getDiffPreferences().then(prefs => {
|
||
element.prefs = prefs;
|
||
element.reload();
|
||
});
|
||
});
|
||
|
||
test('does not render disallowed image type', done => {
|
||
const mockDiff = {
|
||
meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg-evil',
|
||
lines: 560},
|
||
intraline_status: 'OK',
|
||
change_type: 'DELETED',
|
||
diff_header: [
|
||
'diff --git a/carrot.jpg b/carrot.jpg',
|
||
'index f9c2f2c..0000000 100644',
|
||
'--- a/carrot.jpg',
|
||
'+++ /dev/null',
|
||
'Binary files differ',
|
||
],
|
||
content: [{skip: 66}],
|
||
binary: true,
|
||
};
|
||
mockFile1.type = 'image/jpeg-evil';
|
||
|
||
stubs.push(sandbox.stub(element, '_getDiff',
|
||
() => Promise.resolve(mockDiff)));
|
||
|
||
element.addEventListener('render', () => {
|
||
// Recognizes that it should be an image diff.
|
||
assert.isTrue(element.isImageDiff);
|
||
assert.instanceOf(
|
||
element.$.diffBuilder._builder, GrDiffBuilderImage);
|
||
const leftImage = element.$.diffTable.querySelector('td.left img');
|
||
assert.isNotOk(leftImage);
|
||
done();
|
||
});
|
||
|
||
element.$.restAPI.getDiffPreferences().then(prefs => {
|
||
element.prefs = prefs;
|
||
element.reload();
|
||
});
|
||
});
|
||
});
|
||
|
||
test('_handleTap lineNum', done => {
|
||
const addDraftStub = sandbox.stub(element, 'addDraftAtLine');
|
||
const el = document.createElement('div');
|
||
el.className = 'lineNum';
|
||
el.addEventListener('click', e => {
|
||
element._handleTap(e);
|
||
assert.isTrue(addDraftStub.called);
|
||
assert.equal(addDraftStub.lastCall.args[0], el);
|
||
done();
|
||
});
|
||
el.click();
|
||
});
|
||
|
||
test('_handleTap context', done => {
|
||
const showContextStub =
|
||
sandbox.stub(element.$.diffBuilder, 'showContext');
|
||
const el = document.createElement('div');
|
||
el.className = 'showContext';
|
||
el.addEventListener('click', e => {
|
||
element._handleTap(e);
|
||
assert.isTrue(showContextStub.called);
|
||
done();
|
||
});
|
||
el.click();
|
||
});
|
||
|
||
test('_handleTap content', done => {
|
||
const content = document.createElement('div');
|
||
const lineEl = document.createElement('div');
|
||
|
||
const selectStub = sandbox.stub(element, '_selectLine');
|
||
sandbox.stub(element.$.diffBuilder, 'getLineElByChild', () => lineEl);
|
||
|
||
content.className = 'content';
|
||
content.addEventListener('click', e => {
|
||
element._handleTap(e);
|
||
assert.isTrue(selectStub.called);
|
||
assert.equal(selectStub.lastCall.args[0], lineEl);
|
||
done();
|
||
});
|
||
content.click();
|
||
});
|
||
|
||
test('_getDiff handles null diff responses', done => {
|
||
stub('gr-rest-api-interface', {
|
||
getDiff() { return Promise.resolve(null); },
|
||
});
|
||
element.changeNum = 123;
|
||
element.patchRange = {basePatchNum: 1, patchNum: 2};
|
||
element.path = 'file.txt';
|
||
element._getDiff().then(done);
|
||
});
|
||
|
||
suite('getCursorStops', () => {
|
||
const setupDiff = function() {
|
||
const mock = document.createElement('mock-diff-response');
|
||
element._diff = mock.diffResponse;
|
||
element.comments = {
|
||
left: [],
|
||
right: [],
|
||
};
|
||
element.prefs = {
|
||
context: 10,
|
||
tab_size: 8,
|
||
font_size: 12,
|
||
line_length: 100,
|
||
cursor_blink_rate: 0,
|
||
line_wrapping: false,
|
||
intraline_difference: true,
|
||
show_line_endings: true,
|
||
show_tabs: true,
|
||
show_whitespace_errors: true,
|
||
syntax_highlighting: true,
|
||
auto_hide_diff_table_header: true,
|
||
theme: 'DEFAULT',
|
||
ignore_whitespace: 'IGNORE_NONE',
|
||
};
|
||
|
||
element._renderDiffTable();
|
||
flushAsynchronousOperations();
|
||
};
|
||
|
||
test('getCursorStops returns [] when hidden and noAutoRender', () => {
|
||
element.noAutoRender = true;
|
||
setupDiff();
|
||
element.hidden = true;
|
||
assert.equal(element.getCursorStops().length, 0);
|
||
});
|
||
|
||
test('getCursorStops', () => {
|
||
setupDiff();
|
||
assert.equal(element.getCursorStops().length, 50);
|
||
});
|
||
});
|
||
|
||
test('adds .hiddenscroll', () => {
|
||
Gerrit.hiddenscroll = true;
|
||
element.displayLine = true;
|
||
assert.include(element.$$('.diffContainer').className, 'hiddenscroll');
|
||
});
|
||
});
|
||
|
||
suite('logged in', () => {
|
||
let fakeLineEl;
|
||
setup(() => {
|
||
stub('gr-rest-api-interface', {
|
||
getLoggedIn() { return Promise.resolve(true); },
|
||
getPreferences() {
|
||
return Promise.resolve({time_format: 'HHMM_12'});
|
||
},
|
||
});
|
||
element = fixture('basic');
|
||
element.patchRange = {};
|
||
|
||
fakeLineEl = {
|
||
getAttribute: sandbox.stub().returns(42),
|
||
classList: {
|
||
contains: sandbox.stub().returns(true),
|
||
},
|
||
};
|
||
});
|
||
|
||
test('addDraftAtLine', done => {
|
||
sandbox.stub(element, '_selectLine');
|
||
sandbox.stub(element, '_createComment');
|
||
const loggedInErrorSpy = sandbox.spy();
|
||
element.addEventListener('show-auth-required', loggedInErrorSpy);
|
||
element.addDraftAtLine(fakeLineEl);
|
||
flush(() => {
|
||
assert.isFalse(loggedInErrorSpy.called);
|
||
assert.isTrue(element._createComment
|
||
.calledWithExactly(fakeLineEl, 42));
|
||
done();
|
||
});
|
||
});
|
||
|
||
test('addDraftAtLine on an edit', done => {
|
||
element.patchRange.basePatchNum = element.EDIT_NAME;
|
||
sandbox.stub(element, '_selectLine');
|
||
sandbox.stub(element, '_createComment');
|
||
const loggedInErrorSpy = sandbox.spy();
|
||
const alertSpy = sandbox.spy();
|
||
element.addEventListener('show-auth-required', loggedInErrorSpy);
|
||
element.addEventListener('show-alert', alertSpy);
|
||
element.addDraftAtLine(fakeLineEl);
|
||
flush(() => {
|
||
assert.isFalse(loggedInErrorSpy.called);
|
||
assert.isTrue(alertSpy.called);
|
||
assert.isFalse(element._createComment.called);
|
||
done();
|
||
});
|
||
});
|
||
|
||
suite('change in preferences', () => {
|
||
setup(() => {
|
||
element._diff = {
|
||
meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 66},
|
||
meta_b: {name: 'carrot.jpg', content_type: 'image/jpeg',
|
||
lines: 560},
|
||
diff_header: [],
|
||
intraline_status: 'OK',
|
||
change_type: 'MODIFIED',
|
||
content: [{skip: 66}],
|
||
};
|
||
element.comments = {
|
||
meta: {
|
||
changeNum: '42',
|
||
patchRange: {
|
||
basePatchNum: 'PARENT',
|
||
patchNum: 3,
|
||
},
|
||
path: '/path/to/foo',
|
||
projectConfig: {foo: 'bar'},
|
||
},
|
||
left: [
|
||
{id: 'bc1', side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bc2', side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
],
|
||
right: [
|
||
{id: 'c1', __commentSide: 'right'},
|
||
{id: 'c2', __commentSide: 'right'},
|
||
{id: 'd1', __draft: true, __commentSide: 'right'},
|
||
{id: 'd2', __draft: true, __commentSide: 'right'},
|
||
],
|
||
};
|
||
});
|
||
|
||
test('change in preferences re-renders diff', () => {
|
||
sandbox.stub(element, '_renderDiffTable');
|
||
element.prefs = {};
|
||
element.prefs = {time_format: 'HHMM_12'};
|
||
assert.isTrue(element._renderDiffTable.called);
|
||
});
|
||
|
||
test('change in preferences does not re-renders diff with ' +
|
||
'noRenderOnPrefsChange', () => {
|
||
sandbox.stub(element, '_renderDiffTable');
|
||
element.noRenderOnPrefsChange = true;
|
||
element.prefs = {};
|
||
element.prefs = {time_format: 'HHMM_12'};
|
||
assert.isFalse(element._renderDiffTable.called);
|
||
});
|
||
});
|
||
|
||
suite('handle comment-update', () => {
|
||
setup(() => {
|
||
element.comments = {
|
||
meta: {
|
||
changeNum: '42',
|
||
patchRange: {
|
||
basePatchNum: 'PARENT',
|
||
patchNum: 3,
|
||
},
|
||
path: '/path/to/foo',
|
||
projectConfig: {foo: 'bar'},
|
||
},
|
||
left: [
|
||
{id: 'bc1', side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bc2', side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd1', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
{id: 'bd2', __draft: true, side: 'PARENT', __commentSide: 'left'},
|
||
],
|
||
right: [
|
||
{id: 'c1', __commentSide: 'right'},
|
||
{id: 'c2', __commentSide: 'right'},
|
||
{id: 'd1', __draft: true, __commentSide: 'right'},
|
||
{id: 'd2', __draft: true, __commentSide: 'right'},
|
||
],
|
||
};
|
||
});
|
||
|
||
test('creating a draft', () => {
|
||
const comment = {__draft: true, __draftID: 'tempID', side: 'PARENT',
|
||
__commentSide: 'left'};
|
||
element.fire('comment-update', {comment});
|
||
assert.include(element.comments.left, comment);
|
||
});
|
||
|
||
test('saving a draft', () => {
|
||
const draftID = 'tempID';
|
||
const id = 'savedID';
|
||
const comment = {
|
||
__draft: true,
|
||
__draftID: draftID,
|
||
side: 'PARENT',
|
||
__commentSide: 'left',
|
||
};
|
||
element.comments.left.push(comment);
|
||
comment.id = id;
|
||
element.fire('comment-update', {comment});
|
||
const drafts = element.comments.left.filter(item => {
|
||
return item.__draftID === draftID;
|
||
});
|
||
assert.equal(drafts.length, 1);
|
||
assert.equal(drafts[0].id, id);
|
||
});
|
||
});
|
||
});
|
||
|
||
suite('diff header', () => {
|
||
setup(() => {
|
||
element._diff = {
|
||
meta_a: {name: 'carrot.jpg', content_type: 'image/jpeg', lines: 66},
|
||
meta_b: {name: 'carrot.jpg', content_type: 'image/jpeg',
|
||
lines: 560},
|
||
diff_header: [],
|
||
intraline_status: 'OK',
|
||
change_type: 'MODIFIED',
|
||
content: [{skip: 66}],
|
||
};
|
||
});
|
||
|
||
test('hidden', () => {
|
||
assert.equal(element._diffHeaderItems.length, 0);
|
||
element.push('_diff.diff_header', 'diff --git a/test.jpg b/test.jpg');
|
||
assert.equal(element._diffHeaderItems.length, 0);
|
||
element.push('_diff.diff_header', 'index 2adc47d..f9c2f2c 100644');
|
||
assert.equal(element._diffHeaderItems.length, 0);
|
||
element.push('_diff.diff_header', '--- a/test.jpg');
|
||
assert.equal(element._diffHeaderItems.length, 0);
|
||
element.push('_diff.diff_header', '+++ b/test.jpg');
|
||
assert.equal(element._diffHeaderItems.length, 0);
|
||
element.push('_diff.diff_header', 'test');
|
||
assert.equal(element._diffHeaderItems.length, 1);
|
||
flushAsynchronousOperations();
|
||
|
||
assert.equal(element.$.diffHeader.textContent.trim(), 'test');
|
||
element.set('_diff.binary', true);
|
||
assert.equal(element._diffHeaderItems.length, 0);
|
||
});
|
||
});
|
||
|
||
suite('safety and bypass', () => {
|
||
let renderStub;
|
||
|
||
setup(() => {
|
||
element = fixture('basic');
|
||
renderStub = sandbox.stub(element.$.diffBuilder, 'render',
|
||
() => Promise.resolve());
|
||
const mock = document.createElement('mock-diff-response');
|
||
element._diff = mock.diffResponse;
|
||
element.comments = {left: [], right: []};
|
||
element.noRenderOnPrefsChange = true;
|
||
});
|
||
|
||
test('lage render w/ context = 10', () => {
|
||
element.prefs = {context: 10};
|
||
sandbox.stub(element, '_diffLength', () => 10000);
|
||
return element._renderDiffTable().then(() => {
|
||
assert.isTrue(renderStub.called);
|
||
assert.isFalse(element._showWarning);
|
||
});
|
||
});
|
||
|
||
test('lage render w/ whole file and bypass', () => {
|
||
element.prefs = {context: -1};
|
||
element._safetyBypass = 10;
|
||
sandbox.stub(element, '_diffLength', () => 10000);
|
||
return element._renderDiffTable().then(() => {
|
||
assert.isTrue(renderStub.called);
|
||
assert.isFalse(element._showWarning);
|
||
});
|
||
});
|
||
|
||
test('lage render w/ whole file and no bypass', () => {
|
||
element.prefs = {context: -1};
|
||
sandbox.stub(element, '_diffLength', () => 10000);
|
||
return element._renderDiffTable().then(() => {
|
||
assert.isFalse(renderStub.called);
|
||
assert.isTrue(element._showWarning);
|
||
});
|
||
});
|
||
});
|
||
|
||
suite('blame', () => {
|
||
setup(() => {
|
||
element = fixture('basic');
|
||
});
|
||
|
||
test('clearBlame', () => {
|
||
element._blame = [];
|
||
const setBlameSpy = sandbox.spy(element.$.diffBuilder, 'setBlame');
|
||
element.classList.add('showBlame');
|
||
element.clearBlame();
|
||
assert.isNull(element._blame);
|
||
assert.isTrue(setBlameSpy.calledWithExactly(null));
|
||
assert.isFalse(element.classList.contains('showBlame'));
|
||
});
|
||
|
||
test('loadBlame', () => {
|
||
const mockBlame = [{id: 'commit id', ranges: [{start: 1, end: 2}]}];
|
||
const showAlertStub = sinon.stub();
|
||
element.addEventListener('show-alert', showAlertStub);
|
||
const getBlameStub = sandbox.stub(element.$.restAPI, 'getBlame')
|
||
.returns(Promise.resolve(mockBlame));
|
||
element.changeNum = 42;
|
||
element.patchRange = {patchNum: 5, basePatchNum: 4};
|
||
element.path = 'foo/bar.baz';
|
||
return element.loadBlame().then(() => {
|
||
assert.isTrue(getBlameStub.calledWithExactly(
|
||
42, 5, 'foo/bar.baz', true));
|
||
assert.isFalse(showAlertStub.called);
|
||
assert.equal(element._blame, mockBlame);
|
||
assert.isTrue(element.classList.contains('showBlame'));
|
||
});
|
||
});
|
||
|
||
test('loadBlame empty', () => {
|
||
const mockBlame = [];
|
||
const showAlertStub = sinon.stub();
|
||
element.addEventListener('show-alert', showAlertStub);
|
||
sandbox.stub(element.$.restAPI, 'getBlame')
|
||
.returns(Promise.resolve(mockBlame));
|
||
element.changeNum = 42;
|
||
element.patchRange = {patchNum: 5, basePatchNum: 4};
|
||
element.path = 'foo/bar.baz';
|
||
return element.loadBlame()
|
||
.then(() => {
|
||
assert.isTrue(false, 'Promise should not resolve');
|
||
})
|
||
.catch(() => {
|
||
assert.isTrue(showAlertStub.calledOnce);
|
||
assert.isNull(element._blame);
|
||
assert.isFalse(element.classList.contains('showBlame'));
|
||
});
|
||
});
|
||
});
|
||
});
|
||
|
||
a11ySuite('basic');
|
||
</script>
|