David Ostrovsky e6a6f44f8c Render change edits on change and diff screens
ORIGINALLY: Iafd4b80af53624027c347f0f443b4cdfde292e29

This change makes the change view and the diff view change edit aware.
Mutation operations on edit itself, like editing files in editor, are
beyond the scope of this change.

Change edits must be fetched with a separate change edit endpoint and
merged into the change.revisions object. The _number of an editInfo is
'edit'. It has a special property called 'basePatchNum' that marks
which patch set the edit is based on. In patch set selectors, the edit
is sorted right after its basePatchNum.

Alternative implementation considerations:

It could be easier to handle edits on the Polygerrit UI, if

  GET /changes/<id>/detail endpoint

would optionally include the change edit in the resulting
change.revsions map.

TODOs:
* Overwrite change.current_revision with change edit commit in some
  use cases
* Mark the change edit as Change Edit in the header of the change view
* Allow for modification of the edit in the change/diff view
* Disable commenting on files in edit patchsets
* Modify file list rows to have appropriate actions when edit is
  selected (e.g. allow revert, disable marking as reviewed)
* Identify and whitelist valid change/revision actions for an edit 

Bug: Issue 4437
Change-Id: Ia4690d20954de730cd625ac76920e849beb12f7c
2017-08-08 13:44:50 -07:00

137 lines
4.5 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-patch-range-select</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="../../../bower_components/page/page.js"></script>
<link rel="import" href="gr-patch-range-select.html">
<script>void(0);</script>
<test-fixture id="basic">
<template>
<gr-patch-range-select auto></gr-patch-range-select>
</template>
</test-fixture>
<script>
suite('gr-patch-range-select tests', () => {
let element;
let sandbox;
setup(() => {
element = fixture('basic');
sandbox = sinon.sandbox.create();
});
teardown(() => sandbox.restore());
test('enabled/disabled options', () => {
const patchRange = {
basePatchNum: 'PARENT',
patchNum: '3',
};
element._sortedRevisions = [
{_number: 1},
{_number: 2},
{_number: element.EDIT_NAME, basePatchNum: 2},
{_number: 3},
];
for (const patchNum of ['1', '2', '3']) {
assert.isFalse(element._computeRightDisabled(patchNum, patchRange));
}
for (const patchNum of ['PARENT', '1', '2']) {
assert.isFalse(element._computeLeftDisabled(patchNum, patchRange));
}
assert.isTrue(element._computeLeftDisabled('3', patchRange));
patchRange.basePatchNum = element.EDIT_NAME;
assert.isTrue(element._computeLeftDisabled('3', patchRange));
assert.isTrue(element._computeRightDisabled('1', patchRange));
assert.isTrue(element._computeRightDisabled('2', patchRange));
assert.isFalse(element._computeRightDisabled('3', patchRange));
assert.isTrue(element._computeRightDisabled(element.EDIT_NAME, patchRange));
});
test('navigation', done => {
sandbox.stub(element, '_computeLeftDisabled').returns(false);
sandbox.stub(element, '_computeRightDisabled').returns(false);
const showStub = sandbox.stub(page, 'show');
const leftSelectEl = element.$.leftPatchSelect;
const rightSelectEl = element.$.rightPatchSelect;
const blurSpy = sandbox.spy(leftSelectEl, 'blur');
element.changeNum = '42';
element.path = 'path/to/file.txt';
element.availablePatches = ['1', '2', '3'];
element.patchRange = {
basePatchNum: 'PARENT',
patchNum: '3',
};
flushAsynchronousOperations();
let numEvents = 0;
leftSelectEl.addEventListener('change', e => {
numEvents++;
if (numEvents == 1) {
assert(showStub.lastCall.calledWithExactly(
'/c/42/3/path/to/file.txt'),
'Should navigate to /c/42/3/path/to/file.txt');
leftSelectEl.nativeSelect.value = '1';
element.fire('change', {}, {node: leftSelectEl});
assert(blurSpy.called, 'Dropdown should be blurred after selection');
} else if (numEvents == 2) {
assert(showStub.lastCall.calledWithExactly(
'/c/42/1..3/path/to/file.txt'),
'Should navigate to /c/42/1..3/path/to/file.txt');
done();
}
});
leftSelectEl.nativeSelect.value = 'PARENT';
rightSelectEl.nativeSelect.value = '3';
element.fire('change', {}, {node: leftSelectEl});
});
test('filesWeblinks', () => {
element.filesWeblinks = {
meta_a: [
{
name: 'foo',
url: 'f.oo',
},
],
meta_b: [
{
name: 'bar',
url: 'ba.r',
},
],
};
flushAsynchronousOperations();
const domApi = Polymer.dom(element.root);
assert.equal(
domApi.querySelector('a[href="f.oo"]').textContent, 'foo');
assert.equal(
domApi.querySelector('a[href="ba.r"]').textContent, 'bar');
});
});
</script>