Add ability to add annotation layers from plugins
Highlights: * Adds a new getDiffLayers function to gr-js-api-interface.js. This is invoked by gr-diff-builder.html when gathering annotation layers. * New annotationApi function in gr-public-js-api.js for plugins to call. * The annotationApi function returns an instance of the new GrAnnotationActionsInterface in gr-annotation-actions-js-api.js * GrAnnotationActionsInterface has an API for the plugin to register an addLayerFunction and an optional method to call to get a notify callback. * The new samples/coverage-plugin.html is an end-to-end example of how to invoke the new APIs to annotate lines. Bug: Issue 7339 Change-Id: Ie51845e0b3564953aba5d7d41986cedce0337073
This commit is contained in:
parent
3add32d155
commit
af1e0f8bf6
@ -36,6 +36,7 @@ limitations under the License.
|
|||||||
id="processor"
|
id="processor"
|
||||||
groups="{{_groups}}"></gr-diff-processor>
|
groups="{{_groups}}"></gr-diff-processor>
|
||||||
<gr-reporting id="reporting"></gr-reporting>
|
<gr-reporting id="reporting"></gr-reporting>
|
||||||
|
<gr-js-api-interface id="jsAPI"></gr-js-api-interface>
|
||||||
</template>
|
</template>
|
||||||
<script src="../gr-diff/gr-diff-line.js"></script>
|
<script src="../gr-diff/gr-diff-line.js"></script>
|
||||||
<script src="../gr-diff/gr-diff-group.js"></script>
|
<script src="../gr-diff/gr-diff-group.js"></script>
|
||||||
@ -89,6 +90,9 @@ limitations under the License.
|
|||||||
|
|
||||||
properties: {
|
properties: {
|
||||||
diff: Object,
|
diff: Object,
|
||||||
|
diffPath: String,
|
||||||
|
changeNum: String,
|
||||||
|
patchNum: String,
|
||||||
viewMode: String,
|
viewMode: String,
|
||||||
comments: Object,
|
comments: Object,
|
||||||
isImageDiff: Boolean,
|
isImageDiff: Boolean,
|
||||||
@ -111,7 +115,7 @@ limitations under the License.
|
|||||||
|
|
||||||
attached() {
|
attached() {
|
||||||
// Setup annotation layers.
|
// Setup annotation layers.
|
||||||
this._layers = [
|
const layers = [
|
||||||
this._createTrailingWhitespaceLayer(),
|
this._createTrailingWhitespaceLayer(),
|
||||||
this.$.syntaxLayer,
|
this.$.syntaxLayer,
|
||||||
this._createIntralineLayer(),
|
this._createIntralineLayer(),
|
||||||
@ -119,6 +123,14 @@ limitations under the License.
|
|||||||
this.$.rangeLayer,
|
this.$.rangeLayer,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Get layers from plugins (if any).
|
||||||
|
for (const pluginLayer of this.$.jsAPI.getDiffLayers(
|
||||||
|
this.diffPath, this.changeNum, this.patchNum)) {
|
||||||
|
layers.push(pluginLayer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._layers = layers;
|
||||||
|
|
||||||
this.async(() => {
|
this.async(() => {
|
||||||
this._preRenderThread();
|
this._preRenderThread();
|
||||||
});
|
});
|
||||||
|
@ -621,6 +621,33 @@ limitations under the License.
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
suite('layers from plugins', () => {
|
||||||
|
let element;
|
||||||
|
let initialLayersCount;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
element = fixture('basic');
|
||||||
|
element._showTrailingWhitespace = true;
|
||||||
|
initialLayersCount = element._layers.length;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('no plugin layers', () => {
|
||||||
|
const getDiffLayersStub = sinon.stub(element.$.jsAPI, 'getDiffLayers')
|
||||||
|
.returns([]);
|
||||||
|
element.attached();
|
||||||
|
assert.isTrue(getDiffLayersStub.called);
|
||||||
|
assert.equal(element._layers.length, initialLayersCount);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('with plugin layers', () => {
|
||||||
|
const getDiffLayersStub = sinon.stub(element.$.jsAPI, 'getDiffLayers')
|
||||||
|
.returns([{}, {}]);
|
||||||
|
element.attached();
|
||||||
|
assert.isTrue(getDiffLayersStub.called);
|
||||||
|
assert.equal(element._layers.length, initialLayersCount+2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
suite('trailing whitespace', () => {
|
suite('trailing whitespace', () => {
|
||||||
let element;
|
let element;
|
||||||
let layer;
|
let layer;
|
||||||
|
@ -266,6 +266,8 @@ limitations under the License.
|
|||||||
project-name="[[projectName]]"
|
project-name="[[projectName]]"
|
||||||
diff="[[_diff]]"
|
diff="[[_diff]]"
|
||||||
diff-path="[[path]]"
|
diff-path="[[path]]"
|
||||||
|
change-num="[[changeNum]]"
|
||||||
|
patch-num="[[patchRange.patchNum]]"
|
||||||
view-mode="[[viewMode]]"
|
view-mode="[[viewMode]]"
|
||||||
line-wrapping="[[lineWrapping]]"
|
line-wrapping="[[lineWrapping]]"
|
||||||
is-image-diff="[[isImageDiff]]"
|
is-image-diff="[[isImageDiff]]"
|
||||||
|
@ -52,6 +52,7 @@
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
/** @type {?} */
|
||||||
patchRange: Object,
|
patchRange: Object,
|
||||||
path: String,
|
path: String,
|
||||||
prefs: {
|
prefs: {
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright (C) 2017 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.
|
||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to create a context for GrAnnotationActionsInterface.
|
||||||
|
* @param {HTMLElement} el The DIV.contentText element to apply the
|
||||||
|
* annotation to using annotateRange.
|
||||||
|
* @param {GrDiffLine} line The line object.
|
||||||
|
* @param {String} path The file path (eg: /COMMIT_MSG').
|
||||||
|
* @param {String} changeNum The Gerrit change number.
|
||||||
|
* @param {String} patchNum The Gerrit patch number.
|
||||||
|
*/
|
||||||
|
function GrAnnotationActionsContext(el, line, path, changeNum, patchNum) {
|
||||||
|
this._el = el;
|
||||||
|
|
||||||
|
this.line = line;
|
||||||
|
this.path = path;
|
||||||
|
this.changeNum = parseInt(changeNum);
|
||||||
|
this.patchNum = parseInt(patchNum);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method to add annotations to a line.
|
||||||
|
* @param {Number} start The line number where the update starts.
|
||||||
|
* @param {Number} end The line number where the update ends.
|
||||||
|
* @param {String} cssClass The name of a CSS class created using Gerrit.css.
|
||||||
|
* @param {String} side The side of the update. ('left' or 'right')
|
||||||
|
*/
|
||||||
|
GrAnnotationActionsContext.prototype.annotateRange = function(
|
||||||
|
start, end, cssClass, side) {
|
||||||
|
if (this._el.getAttribute('data-side') == side) {
|
||||||
|
GrAnnotation.annotateElement(this._el, start, end, cssClass);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.GrAnnotationActionsContext = GrAnnotationActionsContext;
|
||||||
|
})(window);
|
@ -0,0 +1,77 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
Copyright (C) 2017 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-annotation-actions-context</title>
|
||||||
|
|
||||||
|
<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
|
||||||
|
<script src="../../../bower_components/web-component-tester/browser.js"></script>
|
||||||
|
<script src="../../diff/gr-diff-highlight/gr-annotation.js"></script>
|
||||||
|
|
||||||
|
<link rel="import" href="../../../test/common-test-setup.html"/>
|
||||||
|
<link rel="import" href="gr-js-api-interface.html"/>
|
||||||
|
|
||||||
|
<script>void(0);</script>
|
||||||
|
|
||||||
|
<test-fixture id="basic">
|
||||||
|
<template>
|
||||||
|
<div></div>
|
||||||
|
</template>
|
||||||
|
</test-fixture>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('gr-annotation-actions-context tests', () => {
|
||||||
|
let instance;
|
||||||
|
let sandbox;
|
||||||
|
let el;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
const str = 'lorem ipsum blah blah';
|
||||||
|
const line = {text: str};
|
||||||
|
el = document.createElement('div');
|
||||||
|
el.textContent = str;
|
||||||
|
el.setAttribute('data-side', 'right');
|
||||||
|
instance = new GrAnnotationActionsContext(
|
||||||
|
el, line, 'dummy/path', '123', '1');
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test annotateRange', () => {
|
||||||
|
annotateElementSpy = sandbox.spy(GrAnnotation, 'annotateElement');
|
||||||
|
const start = 0;
|
||||||
|
const end = 100;
|
||||||
|
const cssClass = Gerrit.css('background-color: #000000');
|
||||||
|
|
||||||
|
// Assert annotateElement is not called when side is different.
|
||||||
|
instance.annotateRange(start, end, cssClass, 'left');
|
||||||
|
assert.equal(annotateElementSpy.callCount, 0);
|
||||||
|
|
||||||
|
// Assert annotateElement is called once when side is the same.
|
||||||
|
instance.annotateRange(start, end, cssClass, 'right');
|
||||||
|
assert.equal(annotateElementSpy.callCount, 1);
|
||||||
|
const args = annotateElementSpy.getCalls()[0].args;
|
||||||
|
assert.equal(args[0], el);
|
||||||
|
assert.equal(args[1], start);
|
||||||
|
assert.equal(args[2], end);
|
||||||
|
assert.equal(args[3], cssClass);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
@ -0,0 +1,143 @@
|
|||||||
|
// Copyright (C) 2017 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.
|
||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
function GrAnnotationActionsInterface(plugin) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
// Return this instance when there is an annotatediff event.
|
||||||
|
plugin.on('annotatediff', this);
|
||||||
|
|
||||||
|
// Collect all annotation layers instantiated by getLayer. Will be used when
|
||||||
|
// notifying their listeners in the notify function.
|
||||||
|
this._annotationLayers = [];
|
||||||
|
|
||||||
|
// Default impl is a no-op.
|
||||||
|
this._addLayerFunc = annotationActionsContext => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a function to call to apply annotations. Plugins should use
|
||||||
|
* GrAnnotationActionsContext.annotateRange to apply a CSS class to a range
|
||||||
|
* within a line.
|
||||||
|
* @param {Function<GrAnnotationActionsContext>} addLayerFunc The function
|
||||||
|
* that will be called when the AnnotationLayer is ready to annotate.
|
||||||
|
*/
|
||||||
|
GrAnnotationActionsInterface.prototype.addLayer = function(addLayerFunc) {
|
||||||
|
this._addLayerFunc = addLayerFunc;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The specified function will be called with a notify function for the plugin
|
||||||
|
* to call when it has all required data for annotation. Optional.
|
||||||
|
* @param {Function<Function<String, Number, Number, String>>} notifyFunc See
|
||||||
|
* doc of the notify function below to see what it does.
|
||||||
|
*/
|
||||||
|
GrAnnotationActionsInterface.prototype.addNotifier = function(notifyFunc) {
|
||||||
|
// Register the notify function with the plugin's function.
|
||||||
|
notifyFunc(this.notify.bind(this));
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The notify function will call the listeners of all required annotation
|
||||||
|
* layers. Intended to be called by the plugin when all required data for
|
||||||
|
* annotation is available.
|
||||||
|
* @param {String} path The file path whose listeners should be notified.
|
||||||
|
* @param {Number} start The line where the update starts.
|
||||||
|
* @param {Number} end The line where the update ends.
|
||||||
|
* @param {String} side The side of the update ('left' or 'right').
|
||||||
|
*/
|
||||||
|
GrAnnotationActionsInterface.prototype.notify = function(
|
||||||
|
path, startRange, endRange, side) {
|
||||||
|
for (const annotationLayer of this._annotationLayers) {
|
||||||
|
// Notify only the annotation layer that is associated with the specified
|
||||||
|
// path.
|
||||||
|
if (annotationLayer._path === path) {
|
||||||
|
annotationLayer.notifyListeners(startRange, endRange, side);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be called to register annotation layers by the framework. Not
|
||||||
|
* intended to be called by plugins.
|
||||||
|
* @param {String} path The file path (eg: /COMMIT_MSG').
|
||||||
|
* @param {String} changeNum The Gerrit change number.
|
||||||
|
* @param {String} patchNum The Gerrit patch number.
|
||||||
|
*/
|
||||||
|
GrAnnotationActionsInterface.prototype.getLayer = function(
|
||||||
|
path, changeNum, patchNum) {
|
||||||
|
const annotationLayer = new AnnotationLayer(path, changeNum, patchNum,
|
||||||
|
this._addLayerFunc);
|
||||||
|
this._annotationLayers.push(annotationLayer);
|
||||||
|
return annotationLayer;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to create an instance of the Annotation Layer interface.
|
||||||
|
* @param {String} path The file path (eg: /COMMIT_MSG').
|
||||||
|
* @param {String} changeNum The Gerrit change number.
|
||||||
|
* @param {String} patchNum The Gerrit patch number.
|
||||||
|
* @param {Function<GrAnnotationActionsContext>} addLayerFunc The function
|
||||||
|
* that will be called when the AnnotationLayer is ready to annotate.
|
||||||
|
*/
|
||||||
|
function AnnotationLayer(path, changeNum, patchNum, addLayerFunc) {
|
||||||
|
this._path = path;
|
||||||
|
this._changeNum = changeNum;
|
||||||
|
this._patchNum = patchNum;
|
||||||
|
this._addLayerFunc = addLayerFunc;
|
||||||
|
|
||||||
|
this._listeners = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a listener for layer updates.
|
||||||
|
* @param {Function<Number, Number, String>} fn The update handler function.
|
||||||
|
* Should accept as arguments the line numbers for the start and end of
|
||||||
|
* the update and the side as a string.
|
||||||
|
*/
|
||||||
|
AnnotationLayer.prototype.addListener = function(fn) {
|
||||||
|
this._listeners.push(fn);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Layer method to add annotations to a line.
|
||||||
|
* @param {HTMLElement} el The DIV.contentText element to apply the
|
||||||
|
* annotation to.
|
||||||
|
* @param {GrDiffLine} line The line object.
|
||||||
|
*/
|
||||||
|
AnnotationLayer.prototype.annotate = function(el, line) {
|
||||||
|
const annotationActionsContext = new GrAnnotationActionsContext(
|
||||||
|
el, line, this._path, this._changeNum, this._patchNum);
|
||||||
|
this._addLayerFunc(annotationActionsContext);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify Layer listeners of changes to annotations.
|
||||||
|
* @param {Number} start The line where the update starts.
|
||||||
|
* @param {Number} end The line where the update ends.
|
||||||
|
* @param {String} side The side of the update. ('left' or 'right')
|
||||||
|
*/
|
||||||
|
AnnotationLayer.prototype.notifyListeners = function(
|
||||||
|
startRange, endRange, side) {
|
||||||
|
for (const listener of this._listeners) {
|
||||||
|
listener(startRange, endRange, side);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.GrAnnotationActionsInterface = GrAnnotationActionsInterface;
|
||||||
|
})(window);
|
@ -0,0 +1,133 @@
|
|||||||
|
<!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-annotation-actions-js-api-js-api</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"/>
|
||||||
|
<link rel="import" href="../../change/gr-change-actions/gr-change-actions.html">
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('gr-annotation-actions-js-api tests', () => {
|
||||||
|
let annotationActions;
|
||||||
|
let sandbox;
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
let plugin;
|
||||||
|
Gerrit.install(p => { plugin = p; }, '0.1',
|
||||||
|
'http://test.com/plugins/testplugin/static/test.js');
|
||||||
|
annotationActions = plugin.annotationApi();
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
annotationActions = null;
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('add/get layer', () => {
|
||||||
|
const str = 'lorem ipsum blah blah';
|
||||||
|
const line = {text: str};
|
||||||
|
el = document.createElement('div');
|
||||||
|
el.textContent = str;
|
||||||
|
const changeNum = 1234;
|
||||||
|
const patchNum = 2;
|
||||||
|
let testLayerFuncCalled = false;
|
||||||
|
|
||||||
|
const testLayerFunc = context => {
|
||||||
|
testLayerFuncCalled = true;
|
||||||
|
assert.equal(context.line, line);
|
||||||
|
assert.equal(context.changeNum, changeNum);
|
||||||
|
assert.equal(context.patchNum, 2);
|
||||||
|
};
|
||||||
|
annotationActions.addLayer(testLayerFunc);
|
||||||
|
|
||||||
|
const annotationLayer = annotationActions.getLayer(
|
||||||
|
'/dummy/path', changeNum, patchNum);
|
||||||
|
|
||||||
|
annotationLayer.annotate(el, line);
|
||||||
|
assert.isTrue(testLayerFuncCalled);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('add notifier', () => {
|
||||||
|
const path1 = '/dummy/path1';
|
||||||
|
const path2 = '/dummy/path2';
|
||||||
|
const annotationLayer1 = annotationActions.getLayer(path1, 1, 2);
|
||||||
|
const annotationLayer2 = annotationActions.getLayer(path2, 1, 2);
|
||||||
|
const layer1Spy = sandbox.spy(annotationLayer1, 'notifyListeners');
|
||||||
|
const layer2Spy = sandbox.spy(annotationLayer2, 'notifyListeners');
|
||||||
|
|
||||||
|
let notify;
|
||||||
|
const notifyFunc = n => {
|
||||||
|
notifyFuncCalled = true;
|
||||||
|
notify = n;
|
||||||
|
};
|
||||||
|
annotationActions.addNotifier(notifyFunc);
|
||||||
|
assert.isTrue(notifyFuncCalled);
|
||||||
|
|
||||||
|
// Assert that no layers are invoked with a different path.
|
||||||
|
notify('/dummy/path3', 0, 10, 'right');
|
||||||
|
assert.isFalse(layer1Spy.called);
|
||||||
|
assert.isFalse(layer2Spy.called);
|
||||||
|
|
||||||
|
// Assert that only the 1st layer is invoked with path1.
|
||||||
|
notify(path1, 0, 10, 'right');
|
||||||
|
assert.isTrue(layer1Spy.called);
|
||||||
|
assert.isFalse(layer2Spy.called);
|
||||||
|
|
||||||
|
// Reset spies.
|
||||||
|
layer1Spy.reset();
|
||||||
|
layer2Spy.reset();
|
||||||
|
|
||||||
|
// Assert that only the 2nd layer is invoked with path2.
|
||||||
|
notify(path2, 0, 20, 'left');
|
||||||
|
assert.isFalse(layer1Spy.called);
|
||||||
|
assert.isTrue(layer2Spy.called);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('layer notify listeners', () => {
|
||||||
|
const annotationLayer = annotationActions.getLayer(
|
||||||
|
'/dummy/path', 1, 2);
|
||||||
|
let listenerCalledTimes = 0;
|
||||||
|
const startRange = 10;
|
||||||
|
const endRange = 20;
|
||||||
|
const side = 'right';
|
||||||
|
const listener = (st, end, s) => {
|
||||||
|
listenerCalledTimes++;
|
||||||
|
assert.equal(st, startRange);
|
||||||
|
assert.equal(end, endRange);
|
||||||
|
assert.equal(s, side);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Notify with 0 listeners added.
|
||||||
|
annotationLayer.notifyListeners(startRange, endRange, side);
|
||||||
|
assert.equal(listenerCalledTimes, 0);
|
||||||
|
|
||||||
|
// Add 1 listener.
|
||||||
|
annotationLayer.addListener(listener);
|
||||||
|
annotationLayer.notifyListeners(startRange, endRange, side);
|
||||||
|
assert.equal(listenerCalledTimes, 1);
|
||||||
|
|
||||||
|
// Add 1 more listener. Total 2 listeners.
|
||||||
|
annotationLayer.addListener(listener);
|
||||||
|
annotationLayer.notifyListeners(startRange, endRange, side);
|
||||||
|
assert.equal(listenerCalledTimes, 3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
@ -26,6 +26,8 @@ limitations under the License.
|
|||||||
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
|
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
|
|
||||||
<dom-module id="gr-js-api-interface">
|
<dom-module id="gr-js-api-interface">
|
||||||
|
<script src="gr-annotation-actions-context.js"></script>
|
||||||
|
<script src="gr-annotation-actions-js-api.js"></script>
|
||||||
<script src="gr-change-actions-js-api.js"></script>
|
<script src="gr-change-actions-js-api.js"></script>
|
||||||
<script src="gr-change-reply-js-api.js"></script>
|
<script src="gr-change-reply-js-api.js"></script>
|
||||||
<script src="gr-js-api-interface.js"></script>
|
<script src="gr-js-api-interface.js"></script>
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
COMMENT: 'comment',
|
COMMENT: 'comment',
|
||||||
REVERT: 'revert',
|
REVERT: 'revert',
|
||||||
POST_REVERT: 'postrevert',
|
POST_REVERT: 'postrevert',
|
||||||
|
ANNOTATE_DIFF: 'annotatediff',
|
||||||
};
|
};
|
||||||
|
|
||||||
const Element = {
|
const Element = {
|
||||||
@ -178,6 +179,20 @@
|
|||||||
return revertMsg;
|
return revertMsg;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getDiffLayers(path, changeNum, patchNum) {
|
||||||
|
const layers = [];
|
||||||
|
for (const annotationApi of
|
||||||
|
this._getEventCallbacks(EventType.ANNOTATE_DIFF)) {
|
||||||
|
try {
|
||||||
|
const layer = annotationApi.getLayer(path, changeNum, patchNum);
|
||||||
|
layers.push(layer);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return layers;
|
||||||
|
},
|
||||||
|
|
||||||
getLabelValuesPostRevert(change) {
|
getLabelValuesPostRevert(change) {
|
||||||
let labels = {};
|
let labels = {};
|
||||||
for (const cb of this._getEventCallbacks(EventType.POST_REVERT)) {
|
for (const cb of this._getEventCallbacks(EventType.POST_REVERT)) {
|
||||||
|
@ -175,6 +175,10 @@
|
|||||||
return Gerrit.delete(this.url(url), opt_callback);
|
return Gerrit.delete(this.url(url), opt_callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Plugin.prototype.annotationApi = function() {
|
||||||
|
return new GrAnnotationActionsInterface(this);
|
||||||
|
};
|
||||||
|
|
||||||
Plugin.prototype.changeActions = function() {
|
Plugin.prototype.changeActions = function() {
|
||||||
return new GrChangeActionsInterface(this,
|
return new GrChangeActionsInterface(this,
|
||||||
Plugin._sharedAPIElement.getElement(
|
Plugin._sharedAPIElement.getElement(
|
||||||
|
55
polygerrit-ui/app/samples/coverage-plugin.html
Normal file
55
polygerrit-ui/app/samples/coverage-plugin.html
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<dom-module id="coverage-plugin">
|
||||||
|
<script>
|
||||||
|
|
||||||
|
function populateWithDummyData(coverageData) {
|
||||||
|
coverageData['NewFile'] = {
|
||||||
|
linesMissingCoverage: [1, 2, 3],
|
||||||
|
totalLines: 5,
|
||||||
|
changeNum: 94,
|
||||||
|
patchNum: 2,
|
||||||
|
};
|
||||||
|
coverageData['/COMMIT_MSG'] = {
|
||||||
|
linesMissingCoverage: [3, 4, 7, 14],
|
||||||
|
totalLines: 14,
|
||||||
|
changeNum: 94,
|
||||||
|
patchNum: 2,
|
||||||
|
};
|
||||||
|
coverageData['DEPS'] = {
|
||||||
|
linesMissingCoverage: [3, 4, 7, 14],
|
||||||
|
totalLines: 16,
|
||||||
|
changeNum: 77001,
|
||||||
|
patchNum: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Gerrit.install(plugin => {
|
||||||
|
const coverageData = {};
|
||||||
|
plugin.annotationApi().addNotifier(notifyFunc => {
|
||||||
|
new Promise(resolve => setTimeout(resolve, 3000)).then(
|
||||||
|
() => {
|
||||||
|
populateWithDummyData(coverageData);
|
||||||
|
Object.keys(coverageData).forEach(file => {
|
||||||
|
notifyFunc(file, 0, coverageData[file].totalLines, 'right');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}).addLayer(context => {
|
||||||
|
if (Object.keys(coverageData).length === 0) {
|
||||||
|
// Coverage data is not ready yet.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const path = context.path;
|
||||||
|
const line = context.line;
|
||||||
|
// Highlight lines missing coverage with this background color.
|
||||||
|
const cssClass = Gerrit.css('background-color: #EF9B9B');
|
||||||
|
if (coverageData[path] &&
|
||||||
|
coverageData[path].changeNum === context.changeNum &&
|
||||||
|
coverageData[path].patchNum === context.patchNum) {
|
||||||
|
const linesMissingCoverage = coverageData[path].linesMissingCoverage;
|
||||||
|
if (linesMissingCoverage.includes(line.afterNumber)) {
|
||||||
|
context.annotateRange(0, line.text.length, cssClass, 'right');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</dom-module>
|
Loading…
Reference in New Issue
Block a user