1173 lines
40 KiB
HTML
1173 lines
40 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-file-list</title>
|
|
|
|
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
|
|
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
|
<script src="../../../bower_components/page/page.js"></script>
|
|
<script src="../../../scripts/util.js"></script>
|
|
|
|
<link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
|
|
<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
|
|
<link rel="import" href="gr-file-list.html">
|
|
|
|
<script>void(0);</script>
|
|
|
|
<test-fixture id="basic">
|
|
<template>
|
|
<gr-file-list></gr-file-list>
|
|
</template>
|
|
</test-fixture>
|
|
|
|
<test-fixture id="blank">
|
|
<template>
|
|
<div></div>
|
|
</template>
|
|
</test-fixture>
|
|
|
|
<script>
|
|
suite('gr-file-list tests', function() {
|
|
var element;
|
|
var sandbox;
|
|
var saveStub;
|
|
|
|
setup(function() {
|
|
sandbox = sinon.sandbox.create();
|
|
stub('gr-rest-api-interface', {
|
|
getLoggedIn: function() { return Promise.resolve(true); },
|
|
getPreferences: function() { return Promise.resolve({}); },
|
|
fetchJSON: function() { return Promise.resolve({}); },
|
|
});
|
|
stub('gr-date-formatter', {
|
|
_loadTimeFormat: function() { return Promise.resolve(''); },
|
|
});
|
|
stub('gr-diff', {
|
|
reload: function() { return Promise.resolve(); },
|
|
});
|
|
element = fixture('basic');
|
|
element.numFilesShown = 200;
|
|
saveStub = sandbox.stub(element, '_saveReviewedState',
|
|
function() { return Promise.resolve(); });
|
|
});
|
|
|
|
teardown(function() {
|
|
sandbox.restore();
|
|
});
|
|
|
|
test('correct number of files are shown', function() {
|
|
element._files = _.times(500, function(i) {
|
|
return {__path: '/file' + i, lines_inserted: 9}; });
|
|
flushAsynchronousOperations();
|
|
assert.equal(
|
|
Polymer.dom(element.root).querySelectorAll('.file-row').length,
|
|
element.numFilesShown);
|
|
});
|
|
|
|
test('get file list', function(done) {
|
|
var getChangeFilesStub = sandbox.stub(element.$.restAPI, 'getChangeFiles',
|
|
function() {
|
|
return Promise.resolve({
|
|
'/COMMIT_MSG': {lines_inserted: 9},
|
|
'tags.html': {lines_deleted: 123},
|
|
'about.txt': {},
|
|
});
|
|
});
|
|
|
|
element._getFiles().then(function(files) {
|
|
var filenames = files.map(function(f) { return f.__path; });
|
|
assert.deepEqual(filenames, ['/COMMIT_MSG', 'about.txt', 'tags.html']);
|
|
assert.deepEqual(files[0], {
|
|
lines_inserted: 9,
|
|
lines_deleted: 0,
|
|
__path: '/COMMIT_MSG',
|
|
});
|
|
assert.deepEqual(files[1], {
|
|
lines_inserted: 0,
|
|
lines_deleted: 0,
|
|
__path: 'about.txt',
|
|
});
|
|
assert.deepEqual(files[2], {
|
|
lines_inserted: 0,
|
|
lines_deleted: 123,
|
|
__path: 'tags.html',
|
|
});
|
|
|
|
getChangeFilesStub.restore();
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('calculate totals for patch number', function() {
|
|
element._files = [
|
|
{__path: '/COMMIT_MSG', lines_inserted: 9},
|
|
{
|
|
__path: 'file_added_in_rev2.txt',
|
|
lines_inserted: 1,
|
|
lines_deleted: 1,
|
|
size_delta: 10,
|
|
size: 100,
|
|
},
|
|
{
|
|
__path: 'myfile.txt',
|
|
lines_inserted: 1,
|
|
lines_deleted: 1,
|
|
size_delta: 10,
|
|
size: 100,
|
|
},
|
|
];
|
|
assert.deepEqual(element._patchChange, {
|
|
inserted: 2,
|
|
deleted: 2,
|
|
size_delta_inserted: 0,
|
|
size_delta_deleted: 0,
|
|
total_size: 0,
|
|
});
|
|
assert.isTrue(element._hideBinaryChangeTotals);
|
|
assert.isFalse(element._hideChangeTotals);
|
|
|
|
// Test with a commit message that isn't the first file.
|
|
element._files = [
|
|
{__path: 'file_added_in_rev2.txt', lines_inserted: 1, lines_deleted: 1},
|
|
{__path: '/COMMIT_MSG', lines_inserted: 9},
|
|
{__path: 'myfile.txt', lines_inserted: 1, lines_deleted: 1},
|
|
];
|
|
assert.deepEqual(element._patchChange, {
|
|
inserted: 2,
|
|
deleted: 2,
|
|
size_delta_inserted: 0,
|
|
size_delta_deleted: 0,
|
|
total_size: 0,
|
|
});
|
|
assert.isTrue(element._hideBinaryChangeTotals);
|
|
assert.isFalse(element._hideChangeTotals);
|
|
|
|
// Test with no commit message.
|
|
element._files = [
|
|
{__path: 'file_added_in_rev2.txt', lines_inserted: 1, lines_deleted: 1},
|
|
{__path: 'myfile.txt', lines_inserted: 1, lines_deleted: 1},
|
|
];
|
|
assert.deepEqual(element._patchChange, {
|
|
inserted: 2,
|
|
deleted: 2,
|
|
size_delta_inserted: 0,
|
|
size_delta_deleted: 0,
|
|
total_size: 0,
|
|
});
|
|
assert.isTrue(element._hideBinaryChangeTotals);
|
|
assert.isFalse(element._hideChangeTotals);
|
|
|
|
// Test with files missing either lines_inserted or lines_deleted.
|
|
element._files = [
|
|
{__path: 'file_added_in_rev2.txt', lines_inserted: 1},
|
|
{__path: 'myfile.txt', lines_deleted: 1},
|
|
];
|
|
assert.deepEqual(element._patchChange, {
|
|
inserted: 1,
|
|
deleted: 1,
|
|
size_delta_inserted: 0,
|
|
size_delta_deleted: 0,
|
|
total_size: 0,
|
|
});
|
|
assert.isTrue(element._hideBinaryChangeTotals);
|
|
assert.isFalse(element._hideChangeTotals);
|
|
});
|
|
|
|
test('binary only files', function() {
|
|
element._files = [
|
|
{__path: '/COMMIT_MSG', lines_inserted: 9},
|
|
{__path: 'file_binary', binary: true, size_delta: 10, size: 100},
|
|
{__path: 'file_binary', binary: true, size_delta: -5, size: 120},
|
|
];
|
|
assert.deepEqual(element._patchChange, {
|
|
inserted: 0,
|
|
deleted: 0,
|
|
size_delta_inserted: 10,
|
|
size_delta_deleted: -5,
|
|
total_size: 220,
|
|
});
|
|
assert.isFalse(element._hideBinaryChangeTotals);
|
|
assert.isTrue(element._hideChangeTotals);
|
|
});
|
|
|
|
test('binary and regular files', function() {
|
|
element._files = [
|
|
{__path: '/COMMIT_MSG', lines_inserted: 9},
|
|
{__path: 'file_binary', binary: true, size_delta: 10, size: 100},
|
|
{__path: 'file_binary', binary: true, size_delta: -5, size: 120},
|
|
{__path: 'myfile.txt', lines_deleted: 5, size_delta: -10, size: 100},
|
|
{__path: 'myfile2.txt', lines_inserted: 10},
|
|
];
|
|
assert.deepEqual(element._patchChange, {
|
|
inserted: 10,
|
|
deleted: 5,
|
|
size_delta_inserted: 10,
|
|
size_delta_deleted: -5,
|
|
total_size: 220,
|
|
});
|
|
assert.isFalse(element._hideBinaryChangeTotals);
|
|
assert.isFalse(element._hideChangeTotals);
|
|
});
|
|
|
|
test('_formatBytes function', function() {
|
|
var table = {
|
|
64: '+64 B',
|
|
1023: '+1023 B',
|
|
1024: '+1 KiB',
|
|
4096: '+4 KiB',
|
|
1073741824: '+1 GiB',
|
|
'-64': '-64 B',
|
|
'-1023': '-1023 B',
|
|
'-1024': '-1 KiB',
|
|
'-4096': '-4 KiB',
|
|
'-1073741824': '-1 GiB',
|
|
0: '+/-0 B',
|
|
};
|
|
|
|
for (var bytes in table) {
|
|
if (table.hasOwnProperty(bytes)) {
|
|
assert.equal(element._formatBytes(bytes), table[bytes]);
|
|
}
|
|
}
|
|
});
|
|
|
|
test('_formatPercentage function', function() {
|
|
var table = [
|
|
{ size: 100,
|
|
delta: 100,
|
|
display: '',
|
|
},
|
|
{ size: 195060,
|
|
delta: 64,
|
|
display: '(+0%)',
|
|
},
|
|
{ size: 195060,
|
|
delta: -64,
|
|
display: '(-0%)',
|
|
},
|
|
{ size: 394892,
|
|
delta: -7128,
|
|
display: '(-2%)',
|
|
},
|
|
{ size: 90,
|
|
delta: -10,
|
|
display: '(-10%)',
|
|
},
|
|
{ size: 110,
|
|
delta: 10,
|
|
display: '(+10%)',
|
|
},
|
|
];
|
|
|
|
table.forEach(function(item) {
|
|
assert.equal(element._formatPercentage(
|
|
item.size, item.delta), item.display);
|
|
});
|
|
});
|
|
|
|
suite('keyboard shortcuts', function() {
|
|
setup(function() {
|
|
element._files = [
|
|
{__path: '/COMMIT_MSG'},
|
|
{__path: 'file_added_in_rev2.txt'},
|
|
{__path: 'myfile.txt'},
|
|
];
|
|
element.changeNum = '42';
|
|
element.patchRange = {
|
|
basePatchNum: 'PARENT',
|
|
patchNum: '2',
|
|
};
|
|
element.$.fileCursor.setCursorAtIndex(0);
|
|
});
|
|
|
|
test('toggle left diff via shortcut', function() {
|
|
var toggleLeftDiffStub = sandbox.stub();
|
|
// Property getter cannot be stubbed w/ sandbox due to a bug in Sinon.
|
|
// https://github.com/sinonjs/sinon/issues/781
|
|
var diffsStub = sinon.stub(element, 'diffs', {
|
|
get: function() {
|
|
return [{toggleLeftDiff: toggleLeftDiffStub}];
|
|
},
|
|
});
|
|
MockInteractions.pressAndReleaseKeyOn(element, 65, 'shift', 'a');
|
|
assert.isTrue(toggleLeftDiffStub.calledOnce);
|
|
diffsStub.restore();
|
|
});
|
|
|
|
test('keyboard shortcuts', function() {
|
|
flushAsynchronousOperations();
|
|
|
|
var items = Polymer.dom(element.root).querySelectorAll('.file-row');
|
|
element.$.fileCursor.stops = items;
|
|
element.$.fileCursor.setCursorAtIndex(0);
|
|
assert.equal(items.length, 3);
|
|
assert.isTrue(items[0].classList.contains('selected'));
|
|
assert.isFalse(items[1].classList.contains('selected'));
|
|
assert.isFalse(items[2].classList.contains('selected'));
|
|
// j with a modifier should not move the cursor.
|
|
MockInteractions.pressAndReleaseKeyOn(element, 74, 'shift', 'j');
|
|
assert.equal(element.$.fileCursor.index, 0);
|
|
MockInteractions.pressAndReleaseKeyOn(element, 74, null, 'j');
|
|
assert.equal(element.$.fileCursor.index, 1);
|
|
assert.equal(element.selectedIndex, 1);
|
|
MockInteractions.pressAndReleaseKeyOn(element, 74, null, 'j');
|
|
|
|
var showStub = sandbox.stub(page, 'show');
|
|
assert.equal(element.$.fileCursor.index, 2);
|
|
assert.equal(element.selectedIndex, 2);
|
|
MockInteractions.pressAndReleaseKeyOn(element, 13, null, 'enter');
|
|
assert(showStub.lastCall.calledWith('/c/42/2/myfile.txt'),
|
|
'Should navigate to /c/42/2/myfile.txt');
|
|
// k with a modifier should not move the cursor.
|
|
MockInteractions.pressAndReleaseKeyOn(element, 75, 'shift', 'k');
|
|
assert.equal(element.$.fileCursor.index, 2);
|
|
|
|
MockInteractions.pressAndReleaseKeyOn(element, 75, null, 'k');
|
|
assert.equal(element.$.fileCursor.index, 1);
|
|
assert.equal(element.selectedIndex, 1);
|
|
MockInteractions.pressAndReleaseKeyOn(element, 79, null, 'o');
|
|
assert(showStub.lastCall.calledWith('/c/42/2/file_added_in_rev2.txt'),
|
|
'Should navigate to /c/42/2/file_added_in_rev2.txt');
|
|
|
|
MockInteractions.pressAndReleaseKeyOn(element, 75, null, 'k');
|
|
MockInteractions.pressAndReleaseKeyOn(element, 75, null, 'k');
|
|
MockInteractions.pressAndReleaseKeyOn(element, 75, null, 'k');
|
|
assert.equal(element.$.fileCursor.index, 0);
|
|
assert.equal(element.selectedIndex, 0);
|
|
});
|
|
|
|
test('i key shows/hides selected inline diff', function() {
|
|
sandbox.stub(element, '_expandedPathsChanged');
|
|
flushAsynchronousOperations();
|
|
var files = Polymer.dom(element.root).querySelectorAll('.file-row');
|
|
element.$.fileCursor.stops = files;
|
|
element.$.fileCursor.setCursorAtIndex(0);
|
|
MockInteractions.pressAndReleaseKeyOn(element, 73, null, 'i');
|
|
flushAsynchronousOperations();
|
|
assert.include(element._expandedFilePaths, element.diffs[0].path);
|
|
MockInteractions.pressAndReleaseKeyOn(element, 73, null, 'i');
|
|
flushAsynchronousOperations();
|
|
assert.notInclude(element._expandedFilePaths, element.diffs[0].path);
|
|
element.$.fileCursor.setCursorAtIndex(1);
|
|
MockInteractions.pressAndReleaseKeyOn(element, 73, null, 'i');
|
|
flushAsynchronousOperations();
|
|
assert.include(element._expandedFilePaths, element.diffs[1].path);
|
|
|
|
MockInteractions.pressAndReleaseKeyOn(element, 73, 'shift', 'i');
|
|
flushAsynchronousOperations();
|
|
for (var index in element.diffs) {
|
|
assert.include(element._expandedFilePaths, element.diffs[index].path);
|
|
}
|
|
MockInteractions.pressAndReleaseKeyOn(element, 73, 'shift', 'i');
|
|
flushAsynchronousOperations();
|
|
for (var index in element.diffs) {
|
|
assert.notInclude(element._expandedFilePaths,
|
|
element.diffs[index].path);
|
|
}
|
|
});
|
|
|
|
test('_handleEnterKey navigates', function() {
|
|
sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
|
|
sandbox.stub(element, 'modifierPressed').returns(false);
|
|
var expandStub = sandbox.stub(element, '_openCursorFile');
|
|
var navStub = sandbox.stub(element, '_openSelectedFile');
|
|
var e = new CustomEvent('fake-keyboard-event');
|
|
sinon.stub(e, 'preventDefault');
|
|
element._showInlineDiffs = false;
|
|
element._handleEnterKey(e);
|
|
assert.isTrue(e.preventDefault.called);
|
|
assert.isTrue(navStub.called);
|
|
assert.isFalse(expandStub.called);
|
|
});
|
|
|
|
test('_handleEnterKey expands', function() {
|
|
sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
|
|
sandbox.stub(element, 'modifierPressed').returns(false);
|
|
var expandStub = sandbox.stub(element, '_openCursorFile');
|
|
var navStub = sandbox.stub(element, '_openSelectedFile');
|
|
var e = new CustomEvent('fake-keyboard-event');
|
|
sinon.stub(e, 'preventDefault');
|
|
element._showInlineDiffs = true;
|
|
element._handleEnterKey(e);
|
|
assert.isTrue(e.preventDefault.called);
|
|
assert.isFalse(navStub.called);
|
|
assert.isTrue(expandStub.called);
|
|
});
|
|
|
|
test('_handleEnterKey noop when anchor focused', function() {
|
|
sandbox.stub(element, 'shouldSuppressKeyboardShortcut').returns(false);
|
|
sandbox.stub(element, 'modifierPressed').returns(false);
|
|
var e = new CustomEvent('fake-keyboard-event',
|
|
{detail: {keyboardEvent: {target: document.createElement('a')}}});
|
|
sinon.stub(e, 'preventDefault');
|
|
element._handleEnterKey(e);
|
|
assert.isFalse(e.preventDefault.called);
|
|
});
|
|
});
|
|
|
|
test('comment filtering', function() {
|
|
var comments = {
|
|
'/COMMIT_MSG': [
|
|
{patch_set: 1, message: 'Done', updated: '2017-02-08 16:40:49'},
|
|
{patch_set: 1, message: 'oh hay', updated: '2017-02-09 16:40:49'},
|
|
{patch_set: 2, message: 'hello', updated: '2017-02-10 16:40:49'},
|
|
],
|
|
'myfile.txt': [
|
|
{patch_set: 1, message: 'good news!', updated: '2017-02-08 16:40:49'},
|
|
{patch_set: 2, message: 'wat!?', updated: '2017-02-09 16:40:49'},
|
|
{patch_set: 2, message: 'hi', updated: '2017-02-10 16:40:49'},
|
|
],
|
|
'unresolved.file': [
|
|
{
|
|
patch_set: 2,
|
|
message: 'wat!?',
|
|
updated: '2017-02-09 16:40:49',
|
|
id: '1',
|
|
unresolved: true,
|
|
},
|
|
{
|
|
patch_set: 2,
|
|
message: 'hi',
|
|
updated: '2017-02-10 16:40:49',
|
|
id: '2',
|
|
in_reply_to: '1',
|
|
unresolved: false,
|
|
},
|
|
{
|
|
patch_set: 2,
|
|
message: 'good news!',
|
|
updated: '2017-02-08 16:40:49',
|
|
id: '3',
|
|
unresolved: true,
|
|
},
|
|
],
|
|
};
|
|
var drafts = {
|
|
'unresolved.file': [
|
|
{
|
|
patch_set: 2,
|
|
message: 'hi',
|
|
updated: '2017-02-11 16:40:49',
|
|
id: '4',
|
|
in_reply_to: '3',
|
|
unresolved: false,
|
|
},
|
|
],
|
|
};
|
|
assert.equal(
|
|
element._computeCountString(comments, '1', '/COMMIT_MSG', 'comment'),
|
|
'2 comments');
|
|
assert.equal(
|
|
element._computeCommentsStringMobile(comments, '1', '/COMMIT_MSG'),
|
|
'2c');
|
|
assert.equal(
|
|
element._computeDraftsStringMobile(comments, '1', '/COMMIT_MSG'),
|
|
'2d');
|
|
assert.equal(
|
|
element._computeCountString(comments, '1', 'myfile.txt', 'comment'),
|
|
'1 comment');
|
|
assert.equal(
|
|
element._computeCommentsStringMobile(comments, '1', 'myfile.txt'),
|
|
'1c');
|
|
assert.equal(
|
|
element._computeDraftsStringMobile(comments, '1', 'myfile.txt'),
|
|
'1d');
|
|
assert.equal(
|
|
element._computeCountString(comments, '1',
|
|
'file_added_in_rev2.txt', 'comment'), '');
|
|
assert.equal(
|
|
element._computeCommentsStringMobile(comments, '1',
|
|
'file_added_in_rev2.txt'), '');
|
|
assert.equal(
|
|
element._computeDraftsStringMobile(comments, '1',
|
|
'file_added_in_rev2.txt'), '');
|
|
assert.equal(
|
|
element._computeCountString(comments, '2', '/COMMIT_MSG', 'comment'),
|
|
'1 comment');
|
|
assert.equal(
|
|
element._computeCommentsStringMobile(comments, '2', '/COMMIT_MSG'),
|
|
'1c');
|
|
assert.equal(
|
|
element._computeDraftsStringMobile(comments, '2', '/COMMIT_MSG'),
|
|
'1d');
|
|
assert.equal(
|
|
element._computeCountString(comments, '2', 'myfile.txt', 'comment'),
|
|
'2 comments');
|
|
assert.equal(
|
|
element._computeCommentsStringMobile(comments, '2', 'myfile.txt'),
|
|
'2c');
|
|
assert.equal(
|
|
element._computeDraftsStringMobile(comments, '2', 'myfile.txt'),
|
|
'2d');
|
|
assert.equal(
|
|
element._computeCountString(comments, '2',
|
|
'file_added_in_rev2.txt', 'comment'), '');
|
|
assert.equal(element._computeCountString(comments, '2',
|
|
'unresolved.file', 'comment'), '3 comments');
|
|
assert.equal(
|
|
element._computeUnresolvedString(comments, [], 2, 'myfile.txt'), '');
|
|
assert.equal(
|
|
element._computeUnresolvedString(comments, [], 2, 'unresolved.file'),
|
|
'(1 unresolved)');
|
|
assert.equal(
|
|
element._computeUnresolvedString(comments, drafts, 2,
|
|
'unresolved.file'), '');
|
|
});
|
|
|
|
test('computed properties', function() {
|
|
assert.equal(element._computeFileStatus('A'), 'A');
|
|
assert.equal(element._computeFileStatus(undefined), 'M');
|
|
assert.equal(element._computeFileStatus(null), 'M');
|
|
|
|
assert.equal(element._computeFileDisplayName('/foo/bar/baz'),
|
|
'/foo/bar/baz');
|
|
assert.equal(element._computeFileDisplayName('/COMMIT_MSG'),
|
|
'Commit message');
|
|
|
|
assert.equal(element._computeClass('clazz', '/foo/bar/baz'), 'clazz');
|
|
assert.equal(element._computeClass('clazz', '/COMMIT_MSG'),
|
|
'clazz invisible');
|
|
assert.equal(element._computeExpandInlineClass(
|
|
{expand_inline_diffs: true}), 'expandInline');
|
|
assert.equal(element._computeExpandInlineClass(
|
|
{expand_inline_diffs: false}), '');
|
|
});
|
|
|
|
test('file review status', function() {
|
|
element._files = [
|
|
{__path: '/COMMIT_MSG'},
|
|
{__path: 'file_added_in_rev2.txt'},
|
|
{__path: 'myfile.txt'},
|
|
];
|
|
element._reviewed = ['/COMMIT_MSG', 'myfile.txt'];
|
|
element._loggedIn = true;
|
|
element.changeNum = '42';
|
|
element.patchRange = {
|
|
basePatchNum: 'PARENT',
|
|
patchNum: '2',
|
|
};
|
|
element.$.fileCursor.setCursorAtIndex(0);
|
|
|
|
flushAsynchronousOperations();
|
|
var fileRows =
|
|
Polymer.dom(element.root).querySelectorAll('.row:not(.header)');
|
|
var commitMsg = fileRows[0].querySelector(
|
|
'input.reviewed[type="checkbox"]');
|
|
var fileAdded = fileRows[1].querySelector(
|
|
'input.reviewed[type="checkbox"]');
|
|
var myFile = fileRows[2].querySelector(
|
|
'input.reviewed[type="checkbox"]');
|
|
|
|
assert.isTrue(commitMsg.checked);
|
|
assert.isFalse(fileAdded.checked);
|
|
assert.isTrue(myFile.checked);
|
|
|
|
MockInteractions.tap(commitMsg);
|
|
assert.isTrue(saveStub.lastCall.calledWithExactly('/COMMIT_MSG', false));
|
|
MockInteractions.tap(commitMsg);
|
|
assert.isTrue(saveStub.lastCall.calledWithExactly('/COMMIT_MSG', true));
|
|
});
|
|
|
|
test('patch set from revisions', function() {
|
|
var expected = [
|
|
{num: 1, desc: 'test'},
|
|
{num: 2, desc: 'test'},
|
|
{num: 3, desc: 'test'},
|
|
{num: 4, desc: 'test'},
|
|
];
|
|
var patchNums = element._computePatchSets({
|
|
base: {
|
|
rev3: {_number: 3, description: 'test'},
|
|
rev1: {_number: 1, description: 'test'},
|
|
rev4: {_number: 4, description: 'test'},
|
|
rev2: {_number: 2, description: 'test'},
|
|
},
|
|
});
|
|
assert.equal(patchNums.length, expected.length);
|
|
for (var i = 0; i < expected.length; i++) {
|
|
assert.deepEqual(patchNums[i], expected[i]);
|
|
}
|
|
});
|
|
|
|
test('patch range string', function() {
|
|
assert.equal(
|
|
element._patchRangeStr({basePatchNum: 'PARENT', patchNum: '1'}),
|
|
'1');
|
|
assert.equal(
|
|
element._patchRangeStr({basePatchNum: '1', patchNum: '3'}),
|
|
'1..3');
|
|
});
|
|
|
|
test('diff against dropdown', function(done) {
|
|
var showStub = sandbox.stub(page, 'show');
|
|
element.changeNum = '42';
|
|
element.patchRange = {
|
|
basePatchNum: 'PARENT',
|
|
patchNum: '3',
|
|
};
|
|
element.revisions = {
|
|
rev1: {_number: 1},
|
|
rev2: {_number: 2},
|
|
rev3: {_number: 3},
|
|
};
|
|
flush(function() {
|
|
var selectEl = element.$.patchChange;
|
|
assert.equal(selectEl.value, 'PARENT');
|
|
assert.isTrue(element.$$('option[value="3"]').hasAttribute('disabled'));
|
|
selectEl.addEventListener('change', function() {
|
|
assert.equal(selectEl.value, '2');
|
|
assert(showStub.lastCall.calledWithExactly('/c/42/2..3'),
|
|
'Should navigate to /c/42/2..3');
|
|
showStub.restore();
|
|
done();
|
|
});
|
|
selectEl.value = '2';
|
|
element.fire('change', {}, {node: selectEl});
|
|
});
|
|
});
|
|
|
|
test('checkbox shows/hides diff inline', function() {
|
|
element._files = [
|
|
{__path: 'myfile.txt'},
|
|
];
|
|
element.changeNum = '42';
|
|
element.patchRange = {
|
|
basePatchNum: 'PARENT',
|
|
patchNum: '2',
|
|
};
|
|
element.$.fileCursor.setCursorAtIndex(0);
|
|
sandbox.stub(element, '_expandedPathsChanged');
|
|
flushAsynchronousOperations();
|
|
var fileRows =
|
|
Polymer.dom(element.root).querySelectorAll('.row:not(.header)');
|
|
// Because the label surrounds the input, the tap event is triggered
|
|
// there first.
|
|
var showHideLabel = fileRows[0].querySelector('label.show-hide');
|
|
var showHideCheck = fileRows[0].querySelector(
|
|
'input.show-hide[type="checkbox"]');
|
|
assert.isNotOk(showHideCheck.checked);
|
|
MockInteractions.tap(showHideLabel);
|
|
assert.isOk(showHideCheck.checked);
|
|
assert.notEqual(element._expandedFilePaths.indexOf('myfile.txt'), -1);
|
|
});
|
|
|
|
test('path should be properly escaped', function() {
|
|
element._files = [
|
|
{__path: 'foo bar/my+file.txt%'},
|
|
];
|
|
element.changeNum = '42';
|
|
element.patchRange = {
|
|
basePatchNum: 'PARENT',
|
|
patchNum: '2',
|
|
};
|
|
flushAsynchronousOperations();
|
|
// Slashes should be preserved, and spaces should be translated to `+`.
|
|
// @see Issue 4255 regarding double-encoding.
|
|
// @see Issue 4577 regarding more readable URLs.
|
|
assert.equal(
|
|
element.$$('a').getAttribute('href'),
|
|
'/c/42/2/foo+bar/my%252Bfile.txt%2525');
|
|
});
|
|
|
|
test('diff mode correctly toggles the diffs', function() {
|
|
element._files = [
|
|
{__path: 'myfile.txt'},
|
|
];
|
|
element.changeNum = '42';
|
|
element.patchRange = {
|
|
basePatchNum: 'PARENT',
|
|
patchNum: '2',
|
|
};
|
|
element.$.fileCursor.setCursorAtIndex(0);
|
|
flushAsynchronousOperations();
|
|
|
|
// Tap on a file to generate the diff.
|
|
var row = Polymer.dom(element.root)
|
|
.querySelectorAll('.row:not(.header) label.show-hide')[0];
|
|
|
|
MockInteractions.tap(row);
|
|
flushAsynchronousOperations();
|
|
var diffDisplay = element.diffs[0];
|
|
element._userPrefs = {default_diff_view: 'SIDE_BY_SIDE'};
|
|
assert.equal(element.diffViewMode, 'SIDE_BY_SIDE');
|
|
assert.equal(diffDisplay.viewMode, 'SIDE_BY_SIDE');
|
|
element.set('diffViewMode', 'UNIFIED_DIFF');
|
|
assert.equal(diffDisplay.viewMode, 'UNIFIED_DIFF');
|
|
});
|
|
|
|
test('diff mode selector initializes from preferences', function() {
|
|
var resolvePrefs;
|
|
var prefsPromise = new Promise(function(resolve) {
|
|
resolvePrefs = resolve;
|
|
});
|
|
sandbox.stub(element, '_getPreferences').returns(prefsPromise);
|
|
|
|
// Attach a new gr-file-list so we can intercept the preferences fetch.
|
|
var view = document.createElement('gr-file-list');
|
|
var select = view.$.modeSelect;
|
|
fixture('blank').appendChild(view);
|
|
flushAsynchronousOperations();
|
|
|
|
// At this point the diff mode doesn't yet have the user's preference.
|
|
assert.equal(select.value, 'SIDE_BY_SIDE');
|
|
|
|
// Receive the overriding preference.
|
|
resolvePrefs({default_diff_view: 'UNIFIED'});
|
|
flushAsynchronousOperations();
|
|
assert.equal(select.value, 'SIDE_BY_SIDE');
|
|
document.getElementById('blank').restore();
|
|
});
|
|
|
|
test('show/hide diffs disabled for large amounts of files', function(done) {
|
|
var computeSpy = sandbox.spy(element, '_fileListActionsVisible');
|
|
element._files = [];
|
|
element.changeNum = '42';
|
|
element.patchRange = {
|
|
basePatchNum: 'PARENT',
|
|
patchNum: '2',
|
|
};
|
|
element.$.fileCursor.setCursorAtIndex(0);
|
|
flush(function() {
|
|
assert.isTrue(computeSpy.lastCall.returnValue);
|
|
var arr = [];
|
|
_.times(element._maxFilesForBulkActions + 1, function() {
|
|
arr.push({__path: 'myfile.txt'});
|
|
});
|
|
element._files = arr;
|
|
element.numFilesShown = arr.length;
|
|
assert.isFalse(computeSpy.lastCall.returnValue);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('expanded attribute not set on path when not expanded', function() {
|
|
element._files = [
|
|
{__path: '/COMMIT_MSG'},
|
|
];
|
|
assert.isNotOk(element.$$('.expanded'));
|
|
});
|
|
|
|
test('_getDiffViewMode', function() {
|
|
// No user prefs or diff view mode set.
|
|
assert.equal(element._getDiffViewMode(), 'SIDE_BY_SIDE');
|
|
// User prefs but no diff view mode set.
|
|
element.diffViewMode = null;
|
|
element._userPrefs = {default_diff_view: 'UNIFIED_DIFF'};
|
|
assert.equal(
|
|
element._getDiffViewMode(null, element._userPrefs), 'UNIFIED_DIFF');
|
|
// User prefs and diff view mode set.
|
|
element.diffViewMode = 'SIDE_BY_SIDE';
|
|
assert.equal(element._getDiffViewMode(
|
|
element.diffViewMode, element._userPrefs), 'SIDE_BY_SIDE');
|
|
});
|
|
test('expand_inline_diffs user preference', function() {
|
|
element._files = [
|
|
{__path: '/COMMIT_MSG'},
|
|
];
|
|
element.changeNum = '42';
|
|
element.patchRange = {
|
|
basePatchNum: 'PARENT',
|
|
patchNum: '2',
|
|
};
|
|
sandbox.stub(element, '_expandedPathsChanged');
|
|
flushAsynchronousOperations();
|
|
var commitMsgFile = Polymer.dom(element.root)
|
|
.querySelectorAll('.row:not(.header) a')[0];
|
|
|
|
// Remove href attribute so the app doesn't route to a diff view
|
|
commitMsgFile.removeAttribute('href');
|
|
var togglePathSpy = sandbox.spy(element, '_togglePathExpanded');
|
|
|
|
MockInteractions.tap(commitMsgFile);
|
|
flushAsynchronousOperations();
|
|
assert(togglePathSpy.notCalled, 'file is opened as diff view');
|
|
assert.isNotOk(element.$$('.expanded'));
|
|
|
|
element._userPrefs = {expand_inline_diffs: true};
|
|
flushAsynchronousOperations();
|
|
MockInteractions.tap(commitMsgFile);
|
|
flushAsynchronousOperations();
|
|
assert(togglePathSpy.calledOnce, 'file is expanded');
|
|
assert.isOk(element.$$('.expanded'));
|
|
});
|
|
|
|
test('_togglePathExpanded', function() {
|
|
var path = 'path/to/my/file.txt';
|
|
element.files = [{__path: path}];
|
|
var renderStub = sandbox.stub(element, '_renderInOrder')
|
|
.returns(Promise.resolve());
|
|
|
|
assert.equal(element._expandedFilePaths.length, 0);
|
|
element._togglePathExpanded(path);
|
|
flushAsynchronousOperations();
|
|
|
|
assert.equal(renderStub.callCount, 1);
|
|
assert.include(element._expandedFilePaths, path);
|
|
element._togglePathExpanded(path);
|
|
flushAsynchronousOperations();
|
|
|
|
assert.equal(renderStub.callCount, 2);
|
|
assert.notInclude(element._expandedFilePaths, path);
|
|
});
|
|
|
|
test('_expandedPathsChanged', function(done) {
|
|
sandbox.stub(element, '_reviewFile');
|
|
var path = 'path/to/my/file.txt';
|
|
var diffs = [{
|
|
path: path,
|
|
reload: function() {
|
|
done();
|
|
},
|
|
}];
|
|
var diffsStub = sinon.stub(element, 'diffs', {
|
|
get: function() { return diffs; },
|
|
});
|
|
element.push('_expandedFilePaths', path);
|
|
});
|
|
|
|
suite('_handleFileListTap', function() {
|
|
function testForModifier(modifier) {
|
|
var e = {preventDefault: function() {}};
|
|
e.detail = {sourceEvent: {}};
|
|
e.target = {
|
|
dataset: {path: '/test'},
|
|
classList: element.classList,
|
|
};
|
|
|
|
e.detail.sourceEvent[modifier] = true;
|
|
|
|
var togglePathStub = sandbox.stub(element, '_togglePathExpanded');
|
|
element._userPrefs = { expand_inline_diffs: true };
|
|
|
|
element._handleFileListTap(e);
|
|
assert.isFalse(togglePathStub.called);
|
|
|
|
e.detail.sourceEvent[modifier] = false;
|
|
element._handleFileListTap(e);
|
|
assert.equal(togglePathStub.callCount, 1);
|
|
|
|
element._userPrefs = { expand_inline_diffs: false };
|
|
element._handleFileListTap(e);
|
|
assert.equal(togglePathStub.callCount, 1);
|
|
}
|
|
|
|
test('_handleFileListTap meta', function() {
|
|
testForModifier('metaKey');
|
|
});
|
|
|
|
test('_handleFileListTap ctrl', function() {
|
|
testForModifier('ctrlKey');
|
|
});
|
|
});
|
|
|
|
test('_renderInOrder', function(done) {
|
|
var reviewStub = sandbox.stub(element, '_reviewFile');
|
|
var callCount = 0;
|
|
var diffs = [{
|
|
path: 'p0',
|
|
reload: function() {
|
|
assert.equal(callCount++, 2);
|
|
return Promise.resolve();
|
|
},
|
|
}, {
|
|
path: 'p1',
|
|
reload: function() {
|
|
assert.equal(callCount++, 1);
|
|
return Promise.resolve();
|
|
},
|
|
}, {
|
|
path: 'p2',
|
|
reload: function() {
|
|
assert.equal(callCount++, 0);
|
|
return Promise.resolve();
|
|
},
|
|
}];
|
|
element._renderInOrder(['p2', 'p1', 'p0'], diffs, 3)
|
|
.then(function() {
|
|
assert.isFalse(reviewStub.called);
|
|
done();
|
|
});
|
|
});
|
|
|
|
test('_renderInOrder logged in', function(done) {
|
|
element._isLoggedIn = true;
|
|
var reviewStub = sandbox.stub(element, '_reviewFile');
|
|
var callCount = 0;
|
|
var diffs = [{
|
|
path: 'p0',
|
|
reload: function() {
|
|
assert.equal(reviewStub.callCount, 2);
|
|
assert.equal(callCount++, 2);
|
|
return Promise.resolve();
|
|
},
|
|
}, {
|
|
path: 'p1',
|
|
reload: function() {
|
|
assert.equal(reviewStub.callCount, 1);
|
|
assert.equal(callCount++, 1);
|
|
return Promise.resolve();
|
|
},
|
|
}, {
|
|
path: 'p2',
|
|
reload: function() {
|
|
assert.equal(reviewStub.callCount, 0);
|
|
assert.equal(callCount++, 0);
|
|
return Promise.resolve();
|
|
},
|
|
}];
|
|
element._renderInOrder(['p2', 'p1', 'p0'], diffs, 3)
|
|
.then(function() {
|
|
assert.equal(reviewStub.callCount, 3);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
suite('gr-file-list inline diff tests', function() {
|
|
var element;
|
|
var sandbox;
|
|
|
|
var setupDiff = function(diff) {
|
|
var mock = document.createElement('mock-diff-response');
|
|
diff._diff = mock.diffResponse;
|
|
diff._comments = {
|
|
left: [],
|
|
right: [],
|
|
};
|
|
diff.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',
|
|
};
|
|
diff._renderDiffTable();
|
|
};
|
|
|
|
var renderAndGetNewDiffs = function(index) {
|
|
var diffs =
|
|
Polymer.dom(element.root).querySelectorAll('gr-diff');
|
|
|
|
for (var i = index; i < diffs.length; i++) {
|
|
setupDiff(diffs[i]);
|
|
}
|
|
|
|
element._updateDiffCursor();
|
|
element.$.diffCursor.handleDiffUpdate();
|
|
return diffs;
|
|
};
|
|
|
|
setup(function() {
|
|
sandbox = sinon.sandbox.create();
|
|
stub('gr-rest-api-interface', {
|
|
getLoggedIn: function() { return Promise.resolve(true); },
|
|
getPreferences: function() { return Promise.resolve({}); },
|
|
});
|
|
stub('gr-date-formatter', {
|
|
_loadTimeFormat: function() { return Promise.resolve(''); },
|
|
});
|
|
stub('gr-diff', {
|
|
reload: function() { return Promise.resolve(); },
|
|
});
|
|
element = fixture('basic');
|
|
element.numFilesShown = 75;
|
|
element.selectedIndex = 0;
|
|
element._files = [
|
|
{__path: '/COMMIT_MSG', lines_inserted: 9},
|
|
{
|
|
__path: 'file_added_in_rev2.txt',
|
|
lines_inserted: 1,
|
|
lines_deleted: 1,
|
|
size_delta: 10,
|
|
size: 100,
|
|
},
|
|
{
|
|
__path: 'myfile.txt',
|
|
lines_inserted: 1,
|
|
lines_deleted: 1,
|
|
size_delta: 10,
|
|
size: 100,
|
|
},
|
|
];
|
|
element._reviewed = ['/COMMIT_MSG', 'myfile.txt'];
|
|
element._loggedIn = true;
|
|
element.changeNum = '42';
|
|
element.patchRange = {
|
|
basePatchNum: 'PARENT',
|
|
patchNum: '2',
|
|
};
|
|
sandbox.stub(window, 'fetch', function() {
|
|
return Promise.resolve();
|
|
});
|
|
flushAsynchronousOperations();
|
|
});
|
|
|
|
teardown(function() {
|
|
sandbox.restore();
|
|
});
|
|
|
|
test('cursor with individually opened files', function() {
|
|
MockInteractions.pressAndReleaseKeyOn(element, 73, null, 'i');
|
|
flushAsynchronousOperations();
|
|
var diffs = renderAndGetNewDiffs(0);
|
|
var diffStops = diffs[0].getCursorStops();
|
|
|
|
// 1 diff should be rendered.
|
|
assert.equal(diffs.length, 1);
|
|
|
|
// No line number is selected.
|
|
assert.isFalse(diffStops[10].classList.contains('target-row'));
|
|
|
|
// Tapping content on a line selects the line number.
|
|
MockInteractions.tap(Polymer.dom(
|
|
diffStops[10]).querySelectorAll('.contentText')[0]);
|
|
flushAsynchronousOperations();
|
|
assert.isTrue(diffStops[10].classList.contains('target-row'));
|
|
|
|
// Keyboard shortcuts are still moving the file cursor, not the diff
|
|
// cursor.
|
|
MockInteractions.pressAndReleaseKeyOn(element, 74, null, 'j');
|
|
flushAsynchronousOperations();
|
|
assert.isTrue(diffStops[10].classList.contains('target-row'));
|
|
assert.isFalse(diffStops[11].classList.contains('target-row'));
|
|
|
|
// The file cusor is now at 1.
|
|
assert.equal(element.$.fileCursor.index, 1);
|
|
MockInteractions.pressAndReleaseKeyOn(element, 73, null, 'i');
|
|
flushAsynchronousOperations();
|
|
|
|
diffs = renderAndGetNewDiffs(1);
|
|
// Two diffs should be rendered.
|
|
assert.equal(diffs.length, 2);
|
|
var diffStopsFirst = diffs[0].getCursorStops();
|
|
var diffStopsSecond = diffs[1].getCursorStops();
|
|
|
|
// The line on the first diff is stil selected
|
|
assert.isTrue(diffStopsFirst[10].classList.contains('target-row'));
|
|
assert.isFalse(diffStopsSecond[10].classList.contains('target-row'));
|
|
});
|
|
|
|
test('cursor with toggle all files', function() {
|
|
MockInteractions.pressAndReleaseKeyOn(element, 73, 'shift', 'i');
|
|
flushAsynchronousOperations();
|
|
|
|
var diffs = renderAndGetNewDiffs(0);
|
|
var diffStops = diffs[0].getCursorStops();
|
|
|
|
// 1 diff should be rendered.
|
|
assert.equal(diffs.length, 3);
|
|
|
|
// No line number is selected.
|
|
assert.isFalse(diffStops[10].classList.contains('target-row'));
|
|
|
|
// Tapping content on a line selects the line number.
|
|
MockInteractions.tap(Polymer.dom(
|
|
diffStops[10]).querySelectorAll('.contentText')[0]);
|
|
flushAsynchronousOperations();
|
|
assert.isTrue(diffStops[10].classList.contains('target-row'));
|
|
|
|
// Keyboard shortcuts are still moving the file cursor, not the diff
|
|
// cursor.
|
|
MockInteractions.pressAndReleaseKeyOn(element, 74, null, 'j');
|
|
flushAsynchronousOperations();
|
|
assert.isFalse(diffStops[10].classList.contains('target-row'));
|
|
assert.isTrue(diffStops[11].classList.contains('target-row'));
|
|
|
|
// The file cusor is still at 0.
|
|
assert.equal(element.$.fileCursor.index, 0);
|
|
});
|
|
|
|
suite('n key presses', function() {
|
|
var nKeySpy;
|
|
var nextCommentStub;
|
|
var nextChunkStub;
|
|
var fileRows;
|
|
setup(function() {
|
|
nKeySpy = sandbox.spy(element, '_handleNKey');
|
|
nextCommentStub = sandbox.stub(element.$.diffCursor,
|
|
'moveToNextCommentThread');
|
|
nextChunkStub = sandbox.stub(element.$.diffCursor,
|
|
'moveToNextChunk');
|
|
fileRows =
|
|
Polymer.dom(element.root).querySelectorAll('.row:not(.header)');
|
|
});
|
|
test('n key with all files expanded and no shift key', function() {
|
|
MockInteractions.pressAndReleaseKeyOn(fileRows[0], 73, null, 'i');
|
|
flushAsynchronousOperations();
|
|
|
|
// Handle N key should return before calling diff cursor functions.
|
|
MockInteractions.pressAndReleaseKeyOn(element, 78, null, 'n');
|
|
assert.isTrue(nKeySpy.called);
|
|
assert.isFalse(nextCommentStub.called);
|
|
|
|
// This is also called in diffCursor.moveToFirstChunk.
|
|
assert.equal(nextChunkStub.callCount, 1);
|
|
assert.isFalse(!!element._showInlineDiffs);
|
|
});
|
|
|
|
test('n key with all files expanded and shift key', function() {
|
|
MockInteractions.pressAndReleaseKeyOn(fileRows[0], 73, null, 'i');
|
|
flushAsynchronousOperations();
|
|
|
|
MockInteractions.pressAndReleaseKeyOn(element, 78, 'shift', 'n');
|
|
assert.isTrue(nKeySpy.called);
|
|
assert.isFalse(nextCommentStub.called);
|
|
|
|
// This is also called in diffCursor.moveToFirstChunk.
|
|
assert.equal(nextChunkStub.callCount, 1);
|
|
assert.isFalse(!!element._showInlineDiffs);
|
|
});
|
|
|
|
test('n key without all files expanded and shift key', function() {
|
|
MockInteractions.pressAndReleaseKeyOn(fileRows[0], 73, 'shift', 'i');
|
|
flushAsynchronousOperations();
|
|
|
|
MockInteractions.pressAndReleaseKeyOn(element, 78, null, 'n');
|
|
assert.isTrue(nKeySpy.called);
|
|
assert.isFalse(nextCommentStub.called);
|
|
|
|
// This is also called in diffCursor.moveToFirstChunk.
|
|
assert.equal(nextChunkStub.callCount, 2);
|
|
assert.isTrue(element._showInlineDiffs);
|
|
});
|
|
|
|
test('n key without all files expanded and no shift key', function() {
|
|
MockInteractions.pressAndReleaseKeyOn(fileRows[0], 73, 'shift', 'i');
|
|
flushAsynchronousOperations();
|
|
|
|
MockInteractions.pressAndReleaseKeyOn(element, 78, 'shift', 'n');
|
|
assert.isTrue(nKeySpy.called);
|
|
assert.isTrue(nextCommentStub.called);
|
|
|
|
// This is also called in diffCursor.moveToFirstChunk.
|
|
assert.equal(nextChunkStub.callCount, 1);
|
|
assert.isTrue(element._showInlineDiffs);
|
|
});
|
|
});
|
|
});
|
|
</script>
|