Move comment and thread elements to shared
Even before the refactorings, the comment and thread elements were also used
from views outside of diff/. Now they are now completely independent from
gr-diff and descendants, and can move into their own folder and drop the -diff
from their name.
The dependency left from diff/gr-diff-host onto comment[-thread], makes sense
since gr-diff-host is the Gerrit wrapper for gr-diff with gr-comment[-thread].
Change-Id: I5076428da980198989edc605e5edc0e4d66529dd
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html
deleted file mode 100644
index c3a1de4..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.html
+++ /dev/null
@@ -1,126 +0,0 @@
-<!--
-@license
-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.
--->
-
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
-<link rel="import" href="../../../styles/shared-styles.html">
-<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
-<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
-<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
-<link rel="import" href="../../shared/gr-storage/gr-storage.html">
-<link rel="import" href="../gr-diff-comment/gr-diff-comment.html">
-
-<dom-module id="gr-diff-comment-thread">
- <template>
- <style include="shared-styles">
- gr-button {
- margin-left: .5em;
- }
- #actions {
- margin-left: auto;
- padding: .5em .7em;
- }
- #container {
- background-color: var(--comment-background-color);
- border: 1px solid var(--border-color);
- color: var(--comment-text-color);
- display: block;
- margin-bottom: 1px;
- white-space: normal;
- }
- #container.unresolved {
- background-color: var(--unresolved-comment-background-color);
- }
- #commentInfoContainer {
- border-top: 1px dotted var(--border-color);
- display: flex;
- }
- #unresolvedLabel {
- font-family: var(--font-family);
- margin: auto 0;
- padding: .5em .7em;
- }
- .pathInfo {
- display: flex;
- align-items: baseline;
- }
- .descriptionText {
- margin-left: .5rem;
- font-style: italic;
- }
- </style>
- <template is="dom-if" if="[[showFilePath]]">
- <div class="pathInfo">
- <a href$="[[_getDiffUrlForComment(projectName, changeNum, path, patchNum)]]">[[_computeDisplayPath(path)]]</a>
- <span class="descriptionText">Patchset [[patchNum]]</span>
- </div>
- </template>
- <div id="container" class$="[[_computeHostClass(unresolved)]]">
- <template id="commentList" is="dom-repeat" items="[[_orderedComments]]"
- as="comment">
- <gr-diff-comment
- comment="{{comment}}"
- robot-button-disabled="[[_hideActions(_showActions, _lastComment)]]"
- change-num="[[changeNum]]"
- patch-num="[[patchNum]]"
- draft="[[_isDraft(comment)]]"
- show-actions="[[_showActions]]"
- comment-side="[[comment.__commentSide]]"
- side="[[comment.side]]"
- root-id="[[rootId]]"
- project-config="[[_projectConfig]]"
- on-create-fix-comment="_handleCommentFix"
- on-comment-discard="_handleCommentDiscard"
- on-comment-save="_handleCommentSavedOrDiscarded"></gr-diff-comment>
- </template>
- <div id="commentInfoContainer"
- hidden$="[[_hideActions(_showActions, _lastComment)]]">
- <span id="unresolvedLabel" hidden$="[[!unresolved]]">Unresolved</span>
- <div id="actions">
- <gr-button
- id="replyBtn"
- link
- secondary
- class="action reply"
- on-tap="_handleCommentReply">Reply</gr-button>
- <gr-button
- id="quoteBtn"
- link
- secondary
- class="action quote"
- on-tap="_handleCommentQuote">Quote</gr-button>
- <gr-button
- id="ackBtn"
- link
- secondary
- class="action ack"
- on-tap="_handleCommentAck">Ack</gr-button>
- <gr-button
- id="doneBtn"
- link
- secondary
- class="action done"
- on-tap="_handleCommentDone">Done</gr-button>
- </div>
- </div>
- </div>
- <gr-reporting id="reporting"></gr-reporting>
- <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
- <gr-storage id="storage"></gr-storage>
- </template>
- <script src="gr-diff-comment-thread.js"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
deleted file mode 100644
index a2439d7..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread.js
+++ /dev/null
@@ -1,493 +0,0 @@
-/**
- * @license
- * 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.
- */
-(function() {
- 'use strict';
-
- const UNRESOLVED_EXPAND_COUNT = 5;
- const NEWLINE_PATTERN = /\n/g;
-
- Polymer({
- is: 'gr-diff-comment-thread',
-
- /**
- * Fired when the thread should be discarded.
- *
- * @event thread-discard
- */
-
- /**
- * Fired when a comment in the thread is permanently modified.
- *
- * @event thread-changed
- */
-
- /**
- * gr-diff-comment-thread exposes the following attributes that allow a
- * diff widget like gr-diff to show the thread in the right location:
- *
- * line-num:
- * 1-based line number or undefined if it refers to the entire file.
- *
- * comment-side:
- * "left" or "right". These indicate which of the two diffed versions
- * the comment relates to. In the case of unified diff, the left
- * version is the one whose line number column is further to the left.
- *
- * range:
- * The range of text that the comment refers to (start_line,
- * start_character, end_line, end_character), serialized as JSON. If
- * set, range's end_line will have the same value as line-num. Line
- * numbers are 1-based, char numbers are 0-based. The start position
- * (start_line, start_character) is inclusive, and the end position
- * (end_line, end_character) is exclusive.
- */
- properties: {
- changeNum: String,
- comments: {
- type: Array,
- value() { return []; },
- },
- /**
- * @type {?{start_line: number, start_character: number, end_line: number,
- * end_character: number}}
- */
- range: {
- type: Object,
- reflectToAttribute: true,
- },
- keyEventTarget: {
- type: Object,
- value() { return document.body; },
- },
- commentSide: {
- type: String,
- reflectToAttribute: true,
- },
- patchNum: String,
- path: String,
- projectName: {
- type: String,
- observer: '_projectNameChanged',
- },
- hasDraft: {
- type: Boolean,
- notify: true,
- reflectToAttribute: true,
- },
- isOnParent: {
- type: Boolean,
- value: false,
- },
- parentIndex: {
- type: Number,
- value: null,
- },
- rootId: {
- type: String,
- notify: true,
- computed: '_computeRootId(comments.*)',
- },
- /**
- * If this is true, the comment thread also needs to have the change and
- * line properties property set
- */
- showFilePath: {
- type: Boolean,
- value: false,
- },
- /** Necessary only if showFilePath is true or when used with gr-diff */
- lineNum: {
- type: Number,
- reflectToAttribute: true,
- },
- unresolved: {
- type: Boolean,
- notify: true,
- reflectToAttribute: true,
- },
- _showActions: Boolean,
- _lastComment: Object,
- _orderedComments: Array,
- _projectConfig: Object,
- },
-
- behaviors: [
- Gerrit.KeyboardShortcutBehavior,
- Gerrit.PathListBehavior,
- ],
-
- listeners: {
- 'comment-update': '_handleCommentUpdate',
- },
-
- observers: [
- '_commentsChanged(comments.*)',
- ],
-
- keyBindings: {
- 'e shift+e': '_handleEKey',
- },
-
- attached() {
- this._getLoggedIn().then(loggedIn => {
- this._showActions = loggedIn;
- });
- this._setInitialExpandedState();
- },
-
- addOrEditDraft(opt_lineNum, opt_range) {
- const lastComment = this.comments[this.comments.length - 1] || {};
- if (lastComment.__draft) {
- const commentEl = this._commentElWithDraftID(
- lastComment.id || lastComment.__draftID);
- commentEl.editing = true;
-
- // If the comment was collapsed, re-open it to make it clear which
- // actions are available.
- commentEl.collapsed = false;
- } else {
- const range = opt_range ? opt_range :
- lastComment ? lastComment.range : undefined;
- const unresolved = lastComment ? lastComment.unresolved : undefined;
- this.addDraft(opt_lineNum, range, unresolved);
- }
- },
-
- addDraft(opt_lineNum, opt_range, opt_unresolved) {
- const draft = this._newDraft(opt_lineNum, opt_range);
- draft.__editing = true;
- draft.unresolved = opt_unresolved === false ? opt_unresolved : true;
- this.push('comments', draft);
- },
-
- fireRemoveSelf() {
- this.dispatchEvent(new CustomEvent('thread-discard',
- {detail: {rootId: this.rootId}, bubbles: false}));
- },
-
- _getDiffUrlForComment(projectName, changeNum, path, patchNum) {
- return Gerrit.Nav.getUrlForDiffById(changeNum,
- projectName, path, patchNum,
- null, this.lineNum);
- },
-
- _computeDisplayPath(path) {
- const lineString = this.lineNum ? `#${this.lineNum}` : '';
- return this.computeDisplayPath(path) + lineString;
- },
-
- _getLoggedIn() {
- return this.$.restAPI.getLoggedIn();
- },
-
- _commentsChanged() {
- this._orderedComments = this._sortedComments(this.comments);
- this.updateThreadProperties();
- },
-
- updateThreadProperties() {
- if (this._orderedComments.length) {
- this._lastComment = this._getLastComment();
- this.unresolved = this._lastComment.unresolved;
- this.hasDraft = this._lastComment.__draft;
- }
- },
-
- _hideActions(_showActions, _lastComment) {
- return !_showActions || !_lastComment || !!_lastComment.__draft;
- },
-
- _getLastComment() {
- return this._orderedComments[this._orderedComments.length - 1] || {};
- },
-
- _handleEKey(e) {
- if (this.shouldSuppressKeyboardShortcut(e)) { return; }
-
- // Don’t preventDefault in this case because it will render the event
- // useless for other handlers (other gr-diff-comment-thread elements).
- if (e.detail.keyboardEvent.shiftKey) {
- this._expandCollapseComments(true);
- } else {
- if (this.modifierPressed(e)) { return; }
- this._expandCollapseComments(false);
- }
- },
-
- _expandCollapseComments(actionIsCollapse) {
- const comments =
- Polymer.dom(this.root).querySelectorAll('gr-diff-comment');
- for (const comment of comments) {
- comment.collapsed = actionIsCollapse;
- }
- },
-
- /**
- * Sets the initial state of the comment thread.
- * Expands the thread if one of the following is true:
- * - last {UNRESOLVED_EXPAND_COUNT} comments expanded by default if the
- * thread is unresolved,
- * - it's a robot comment.
- */
- _setInitialExpandedState() {
- if (this._orderedComments) {
- for (let i = 0; i < this._orderedComments.length; i++) {
- const comment = this._orderedComments[i];
- const isRobotComment = !!comment.robot_id;
- // False if it's an unresolved comment under UNRESOLVED_EXPAND_COUNT.
- const resolvedThread = !this.unresolved ||
- this._orderedComments.length - i - 1 >= UNRESOLVED_EXPAND_COUNT;
- comment.collapsed = !isRobotComment && resolvedThread;
- }
- }
- },
-
- _sortedComments(comments) {
- return comments.slice().sort((c1, c2) => {
- const c1Date = c1.__date || util.parseDate(c1.updated);
- const c2Date = c2.__date || util.parseDate(c2.updated);
- const dateCompare = c1Date - c2Date;
- // Ensure drafts are at the end. There should only be one but in edge
- // cases could be more. In the unlikely event two drafts are being
- // compared, use the typical date compare.
- if (c2.__draft && !c1.__draft ) { return -1; }
- if (c1.__draft && !c2.__draft ) { return 1; }
- if (dateCompare === 0 && (!c1.id || !c1.id.localeCompare)) { return 0; }
- // If same date, fall back to sorting by id.
- return dateCompare ? dateCompare : c1.id.localeCompare(c2.id);
- });
- },
-
- _createReplyComment(parent, content, opt_isEditing,
- opt_unresolved) {
- this.$.reporting.recordDraftInteraction();
- const reply = this._newReply(
- this._orderedComments[this._orderedComments.length - 1].id,
- parent.line,
- content,
- opt_unresolved,
- parent.range);
-
- // If there is currently a comment in an editing state, add an attribute
- // so that the gr-diff-comment knows not to populate the draft text.
- for (let i = 0; i < this.comments.length; i++) {
- if (this.comments[i].__editing) {
- reply.__otherEditing = true;
- break;
- }
- }
-
- if (opt_isEditing) {
- reply.__editing = true;
- }
-
- this.push('comments', reply);
-
- if (!opt_isEditing) {
- // Allow the reply to render in the dom-repeat.
- this.async(() => {
- const commentEl = this._commentElWithDraftID(reply.__draftID);
- commentEl.save();
- }, 1);
- }
- },
-
- _isDraft(comment) {
- return !!comment.__draft;
- },
-
- /**
- * @param {boolean=} opt_quote
- */
- _processCommentReply(opt_quote) {
- const comment = this._lastComment;
- let quoteStr;
- if (opt_quote) {
- const msg = comment.message;
- quoteStr = '> ' + msg.replace(NEWLINE_PATTERN, '\n> ') + '\n\n';
- }
- this._createReplyComment(comment, quoteStr, true, comment.unresolved);
- },
-
- _handleCommentReply(e) {
- this._processCommentReply();
- },
-
- _handleCommentQuote(e) {
- this._processCommentReply(true);
- },
-
- _handleCommentAck(e) {
- const comment = this._lastComment;
- this._createReplyComment(comment, 'Ack', false, false);
- },
-
- _handleCommentDone(e) {
- const comment = this._lastComment;
- this._createReplyComment(comment, 'Done', false, false);
- },
-
- _handleCommentFix(e) {
- const comment = e.detail.comment;
- const msg = comment.message;
- const quoteStr = '> ' + msg.replace(NEWLINE_PATTERN, '\n> ') + '\n\n';
- const response = quoteStr + 'Please Fix';
- this._createReplyComment(comment, response, false, true);
- },
-
- _commentElWithDraftID(id) {
- const els = Polymer.dom(this.root).querySelectorAll('gr-diff-comment');
- for (const el of els) {
- if (el.comment.id === id || el.comment.__draftID === id) {
- return el;
- }
- }
- return null;
- },
-
- _newReply(inReplyTo, opt_lineNum, opt_message, opt_unresolved,
- opt_range) {
- const d = this._newDraft(opt_lineNum);
- d.in_reply_to = inReplyTo;
- d.range = opt_range;
- if (opt_message != null) {
- d.message = opt_message;
- }
- if (opt_unresolved !== undefined) {
- d.unresolved = opt_unresolved;
- }
- return d;
- },
-
- /**
- * @param {number=} opt_lineNum
- * @param {!Object=} opt_range
- */
- _newDraft(opt_lineNum, opt_range) {
- const d = {
- __draft: true,
- __draftID: Math.random().toString(36),
- __date: new Date(),
- path: this.path,
- patchNum: this.patchNum,
- side: this._getSide(this.isOnParent),
- __commentSide: this.commentSide,
- };
- if (opt_lineNum) {
- d.line = opt_lineNum;
- }
- if (opt_range) {
- d.range = opt_range;
- }
- if (this.parentIndex) {
- d.parent = this.parentIndex;
- }
- return d;
- },
-
- _getSide(isOnParent) {
- if (isOnParent) { return 'PARENT'; }
- return 'REVISION';
- },
-
- _computeRootId(comments) {
- // Keep the root ID even if the comment was removed, so that notification
- // to sync will know which thread to remove.
- if (!comments.base.length) { return this.rootId; }
- const rootComment = comments.base[0];
- return rootComment.id || rootComment.__draftID;
- },
-
- _handleCommentDiscard(e) {
- const diffCommentEl = Polymer.dom(e).rootTarget;
- const comment = diffCommentEl.comment;
- const idx = this._indexOf(comment, this.comments);
- if (idx == -1) {
- throw Error('Cannot find comment ' +
- JSON.stringify(diffCommentEl.comment));
- }
- this.splice('comments', idx, 1);
- if (this.comments.length === 0) {
- this.fireRemoveSelf();
- }
- this._handleCommentSavedOrDiscarded(e);
-
- // Check to see if there are any other open comments getting edited and
- // set the local storage value to its message value.
- for (const changeComment of this.comments) {
- if (changeComment.__editing) {
- const commentLocation = {
- changeNum: this.changeNum,
- patchNum: this.patchNum,
- path: changeComment.path,
- line: changeComment.line,
- };
- return this.$.storage.setDraftComment(commentLocation,
- changeComment.message);
- }
- }
- },
-
- _handleCommentSavedOrDiscarded(e) {
- this.dispatchEvent(new CustomEvent('thread-changed',
- {detail: {rootId: this.rootId, path: this.path},
- bubbles: false}));
- },
-
- _handleCommentUpdate(e) {
- const comment = e.detail.comment;
- const index = this._indexOf(comment, this.comments);
- if (index === -1) {
- // This should never happen: comment belongs to another thread.
- console.warn('Comment update for another comment thread.');
- return;
- }
- this.set(['comments', index], comment);
- // Because of the way we pass these comment objects around by-ref, in
- // combination with the fact that Polymer does dirty checking in
- // observers, the this.set() call above will not cause a thread update in
- // some situations.
- this.updateThreadProperties();
- },
-
- _indexOf(comment, arr) {
- for (let i = 0; i < arr.length; i++) {
- const c = arr[i];
- if ((c.__draftID != null && c.__draftID == comment.__draftID) ||
- (c.id != null && c.id == comment.id)) {
- return i;
- }
- }
- return -1;
- },
-
- _computeHostClass(unresolved) {
- return unresolved ? 'unresolved' : '';
- },
-
- /**
- * Load the project config when a project name has been provided.
- * @param {string} name The project name.
- */
- _projectNameChanged(name) {
- if (!name) { return; }
- this.$.restAPI.getProjectConfig(name).then(config => {
- this._projectConfig = config;
- });
- },
- });
-})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html
deleted file mode 100644
index 1881497..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread/gr-diff-comment-thread_test.html
+++ /dev/null
@@ -1,751 +0,0 @@
-<!DOCTYPE html>
-<!--
-@license
-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-comment-thread</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="gr-diff-comment-thread.html">
-
-<script>void(0);</script>
-
-<test-fixture id="basic">
- <template>
- <gr-diff-comment-thread></gr-diff-comment-thread>
- </template>
-</test-fixture>
-
-<test-fixture id="withComment">
- <template>
- <gr-diff-comment-thread></gr-diff-comment-thread>
- </template>
-</test-fixture>
-
-<script>
- suite('gr-diff-comment-thread tests', () => {
- let element;
- let sandbox;
-
- setup(() => {
- sandbox = sinon.sandbox.create();
- stub('gr-rest-api-interface', {
- getLoggedIn() { return Promise.resolve(false); },
- });
- sandbox = sinon.sandbox.create();
- element = fixture('basic');
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('comments are sorted correctly', () => {
- const comments = [
- {
- message: 'i like you, too',
- in_reply_to: 'sallys_confession',
- __date: new Date('2015-12-25'),
- }, {
- id: 'sallys_confession',
- message: 'i like you, jack',
- updated: '2015-12-24 15:00:20.396000000',
- }, {
- id: 'sally_to_dr_finklestein',
- message: 'i’m running away',
- updated: '2015-10-31 09:00:20.396000000',
- }, {
- id: 'sallys_defiance',
- in_reply_to: 'sally_to_dr_finklestein',
- message: 'i will poison you so i can get away',
- updated: '2015-10-31 15:00:20.396000000',
- }, {
- id: 'dr_finklesteins_response',
- in_reply_to: 'sally_to_dr_finklestein',
- message: 'no i will pull a thread and your arm will fall off',
- updated: '2015-10-31 11:00:20.396000000',
- }, {
- id: 'sallys_mission',
- message: 'i have to find santa',
- updated: '2015-12-24 15:00:20.396000000',
- },
- ];
- const results = element._sortedComments(comments);
- assert.deepEqual(results, [
- {
- id: 'sally_to_dr_finklestein',
- message: 'i’m running away',
- updated: '2015-10-31 09:00:20.396000000',
- }, {
- id: 'dr_finklesteins_response',
- in_reply_to: 'sally_to_dr_finklestein',
- message: 'no i will pull a thread and your arm will fall off',
- updated: '2015-10-31 11:00:20.396000000',
- }, {
- id: 'sallys_defiance',
- in_reply_to: 'sally_to_dr_finklestein',
- message: 'i will poison you so i can get away',
- updated: '2015-10-31 15:00:20.396000000',
- }, {
- id: 'sallys_confession',
- message: 'i like you, jack',
- updated: '2015-12-24 15:00:20.396000000',
- }, {
- id: 'sallys_mission',
- message: 'i have to find santa',
- updated: '2015-12-24 15:00:20.396000000',
- }, {
- message: 'i like you, too',
- in_reply_to: 'sallys_confession',
- __date: new Date('2015-12-25'),
- },
- ]);
- });
-
- test('addOrEditDraft w/ edit draft', () => {
- element.comments = [{
- id: 'jacks_reply',
- message: 'i like you, too',
- in_reply_to: 'sallys_confession',
- updated: '2015-12-25 15:00:20.396000000',
- __draft: true,
- }];
- const commentElStub = sandbox.stub(element, '_commentElWithDraftID',
- () => { return {}; });
- const addDraftStub = sandbox.stub(element, 'addDraft');
-
- element.addOrEditDraft(123);
-
- assert.isTrue(commentElStub.called);
- assert.isFalse(addDraftStub.called);
- });
-
- test('addOrEditDraft w/o edit draft', () => {
- element.comments = [];
- const commentElStub = sandbox.stub(element, '_commentElWithDraftID',
- () => { return {}; });
- const addDraftStub = sandbox.stub(element, 'addDraft');
-
- element.addOrEditDraft(123);
-
- assert.isFalse(commentElStub.called);
- assert.isTrue(addDraftStub.called);
- });
-
- test('_hideActions', () => {
- let showActions = true;
- const lastComment = {};
- assert.equal(element._hideActions(showActions, lastComment), false);
- showActions = false;
- assert.equal(element._hideActions(showActions, lastComment), true);
- showActions = true;
- lastComment.__draft = true;
- assert.equal(element._hideActions(showActions, lastComment), true);
- });
-
- test('setting project name loads the project config', done => {
- const projectName = 'foo/bar/baz';
- const getProjectStub = sandbox.stub(element.$.restAPI, 'getProjectConfig')
- .returns(Promise.resolve({}));
- element.projectName = projectName;
- flush(() => {
- assert.isTrue(getProjectStub.calledWithExactly(projectName));
- done();
- });
- });
-
- test('optionally show file path', () => {
- // Path info doesn't exist when showFilePath is false. Because it's in a
- // dom-if it is not yet in the dom.
- assert.isNotOk(element.$$('.pathInfo'));
-
- sandbox.stub(Gerrit.Nav, 'getUrlForDiffById');
- element.changeNum = 123;
- element.projectName = 'test project';
- element.path = 'path/to/file';
- element.patchNum = 3;
- element.lineNum = 5;
- element.showFilePath = true;
- flushAsynchronousOperations();
- assert.isOk(element.$$('.pathInfo'));
- assert.notEqual(getComputedStyle(element.$$('.pathInfo')).display,
- 'none');
- assert.isTrue(Gerrit.Nav.getUrlForDiffById.lastCall.calledWithExactly(
- element.changeNum, element.projectName, element.path,
- element.patchNum, null, element.lineNum));
- });
-
- test('_computeDisplayPath', () => {
- const path = 'path/to/file';
- assert.equal(element._computeDisplayPath(path), 'path/to/file');
-
- element.lineNum = 5;
- assert.equal(element._computeDisplayPath(path), 'path/to/file#5');
- });
- });
-
- suite('comment action tests', () => {
- let element;
- let sandbox;
-
- setup(() => {
- sandbox = sinon.sandbox.create();
- stub('gr-rest-api-interface', {
- getLoggedIn() { return Promise.resolve(false); },
- saveDiffDraft() {
- return Promise.resolve({
- ok: true,
- text() {
- return Promise.resolve(')]}\'\n' +
- JSON.stringify({
- id: '7afa4931_de3d65bd',
- path: '/path/to/file.txt',
- line: 5,
- in_reply_to: 'baf0414d_60047215',
- updated: '2015-12-21 02:01:10.850000000',
- message: 'Done',
- }));
- },
- });
- },
- deleteDiffDraft() { return Promise.resolve({ok: true}); },
- });
- element = fixture('withComment');
- element.comments = [{
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 'baf0414d_60047215',
- line: 5,
- message: 'is this a crossover episode!?',
- updated: '2015-12-08 19:48:33.843000000',
- path: '/path/to/file.txt',
- }];
- flushAsynchronousOperations();
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('reply', () => {
- const commentEl = element.$$('gr-diff-comment');
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- assert.ok(commentEl);
-
- const replyBtn = element.$.replyBtn;
- MockInteractions.tap(replyBtn);
- flushAsynchronousOperations();
-
- const drafts = element._orderedComments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.notOk(drafts[0].message, 'message should be empty');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.isTrue(reportStub.calledOnce);
- });
-
- test('quote reply', () => {
- const commentEl = element.$$('gr-diff-comment');
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- assert.ok(commentEl);
-
- const quoteBtn = element.$.quoteBtn;
- MockInteractions.tap(quoteBtn);
- flushAsynchronousOperations();
-
- const drafts = element._orderedComments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.equal(drafts[0].message, '> is this a crossover episode!?\n\n');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.isTrue(reportStub.calledOnce);
- });
-
- test('quote reply multiline', () => {
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- element.comments = [{
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 'baf0414d_60047215',
- line: 5,
- message: 'is this a crossover episode!?\nIt might be!',
- updated: '2015-12-08 19:48:33.843000000',
- }];
- flushAsynchronousOperations();
-
- const commentEl = element.$$('gr-diff-comment');
- assert.ok(commentEl);
-
- const quoteBtn = element.$.quoteBtn;
- MockInteractions.tap(quoteBtn);
- flushAsynchronousOperations();
-
- const drafts = element._orderedComments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.equal(drafts[0].message,
- '> is this a crossover episode!?\n> It might be!\n\n');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.isTrue(reportStub.calledOnce);
- });
-
- test('ack', done => {
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- element.changeNum = '42';
- element.patchNum = '1';
-
- const commentEl = element.$$('gr-diff-comment');
- assert.ok(commentEl);
-
- const ackBtn = element.$.ackBtn;
- MockInteractions.tap(ackBtn);
- flush(() => {
- const drafts = element.comments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.equal(drafts[0].message, 'Ack');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.equal(drafts[0].unresolved, false);
- assert.isTrue(reportStub.calledOnce);
- done();
- });
- });
-
- test('done', done => {
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- element.changeNum = '42';
- element.patchNum = '1';
- const commentEl = element.$$('gr-diff-comment');
- assert.ok(commentEl);
-
- const doneBtn = element.$.doneBtn;
- MockInteractions.tap(doneBtn);
- flush(() => {
- const drafts = element.comments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.equal(drafts[0].message, 'Done');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.isFalse(drafts[0].unresolved);
- assert.isTrue(reportStub.calledOnce);
- done();
- });
- });
-
- test('save', done => {
- element.changeNum = '42';
- element.patchNum = '1';
- element.path = '/path/to/file.txt';
- const commentEl = element.$$('gr-diff-comment');
- assert.ok(commentEl);
-
- const saveOrDiscardStub = sandbox.stub();
- element.addEventListener('thread-changed', saveOrDiscardStub);
- element.$$('gr-diff-comment')._fireSave();
-
- flush(() => {
- assert.isTrue(saveOrDiscardStub.called);
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.rootId,
- 'baf0414d_60047215');
- assert.equal(element.rootId, 'baf0414d_60047215');
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.path,
- '/path/to/file.txt');
- done();
- });
- });
-
- test('please fix', done => {
- element.changeNum = '42';
- element.patchNum = '1';
- const commentEl = element.$$('gr-diff-comment');
- assert.ok(commentEl);
- commentEl.addEventListener('create-fix-comment', () => {
- const drafts = element._orderedComments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 1);
- assert.equal(
- drafts[0].message, '> is this a crossover episode!?\n\nPlease Fix');
- assert.equal(drafts[0].in_reply_to, 'baf0414d_60047215');
- assert.isTrue(drafts[0].unresolved);
- done();
- });
- commentEl.fire('create-fix-comment', {comment: commentEl.comment},
- {bubbles: false});
- });
-
- test('discard', done => {
- element.changeNum = '42';
- element.patchNum = '1';
- element.path = '/path/to/file.txt';
- element.push('comments', element._newReply(
- element.comments[0].id,
- element.comments[0].line,
- element.comments[0].path,
- 'it’s pronouced jiff, not giff'));
- flushAsynchronousOperations();
-
- const saveOrDiscardStub = sandbox.stub();
- element.addEventListener('thread-changed', saveOrDiscardStub);
- const draftEl =
- Polymer.dom(element.root).querySelectorAll('gr-diff-comment')[1];
- assert.ok(draftEl);
- draftEl.addEventListener('comment-discard', () => {
- const drafts = element.comments.filter(c => {
- return c.__draft == true;
- });
- assert.equal(drafts.length, 0);
- assert.isTrue(saveOrDiscardStub.called);
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.rootId,
- element.rootId);
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.path,
- element.path);
- done();
- });
- draftEl.fire('comment-discard', {comment: draftEl.comment},
- {bubbles: false});
- });
-
- test('discard with a single comment still fires event with previous rootId',
- done => {
- element.changeNum = '42';
- element.patchNum = '1';
- element.path = '/path/to/file.txt';
- element.comments = [];
- element.addOrEditDraft('1');
- flushAsynchronousOperations();
- const rootId = element.rootId;
- assert.isOk(rootId);
-
- const saveOrDiscardStub = sandbox.stub();
- element.addEventListener('thread-changed', saveOrDiscardStub);
- const draftEl =
- Polymer.dom(element.root).querySelectorAll('gr-diff-comment')[0];
- assert.ok(draftEl);
- draftEl.addEventListener('comment-discard', () => {
- assert.equal(element.comments.length, 0);
- assert.isTrue(saveOrDiscardStub.called);
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.rootId,
- rootId);
- assert.equal(saveOrDiscardStub.lastCall.args[0].detail.path,
- element.path);
- done();
- });
- draftEl.fire('comment-discard', {comment: draftEl.comment},
- {bubbles: false});
- });
-
- test('first editing comment does not add __otherEditing attribute', () => {
- element.comments = [{
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 'baf0414d_60047215',
- line: 5,
- message: 'is this a crossover episode!?',
- updated: '2015-12-08 19:48:33.843000000',
- __draft: true,
- }];
-
- const replyBtn = element.$.replyBtn;
- MockInteractions.tap(replyBtn);
- flushAsynchronousOperations();
-
- const editing = element._orderedComments.filter(c => {
- return c.__editing == true;
- });
- assert.equal(editing.length, 1);
- assert.equal(!!editing[0].__otherEditing, false);
- });
-
- test('When not editing other comments, local storage not set' +
- ' after discard', done => {
- element.changeNum = '42';
- element.patchNum = '1';
- element.comments = [{
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 'baf0414d_60047215',
- line: 5,
- message: 'is this a crossover episode!?',
- updated: '2015-12-08 19:48:31.843000000',
- },
- {
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- __draftID: '1',
- in_reply_to: 'baf0414d_60047215',
- line: 5,
- message: 'yes',
- updated: '2015-12-08 19:48:32.843000000',
- __draft: true,
- __editing: true,
- },
- {
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- __draftID: '2',
- in_reply_to: 'baf0414d_60047215',
- line: 5,
- message: 'no',
- updated: '2015-12-08 19:48:33.843000000',
- __draft: true,
- }];
- const storageStub = sinon.stub(element.$.storage, 'setDraftComment');
- flushAsynchronousOperations();
-
- const draftEl =
- Polymer.dom(element.root).querySelectorAll('gr-diff-comment')[1];
- assert.ok(draftEl);
- draftEl.addEventListener('comment-discard', () => {
- assert.isFalse(storageStub.called);
- storageStub.restore();
- done();
- });
- draftEl.fire('comment-discard', {comment: draftEl.comment},
- {bubbles: false});
- });
-
- test('comment-update', () => {
- const commentEl = element.$$('gr-diff-comment');
- const updatedComment = {
- id: element.comments[0].id,
- foo: 'bar',
- };
- commentEl.fire('comment-update', {comment: updatedComment});
- assert.strictEqual(element.comments[0], updatedComment);
- });
-
- suite('jack and sally comment data test consolidation', () => {
- setup(() => {
- element.comments = [
- {
- id: 'jacks_reply',
- message: 'i like you, too',
- in_reply_to: 'sallys_confession',
- updated: '2015-12-25 15:00:20.396000000',
- unresolved: false,
- }, {
- id: 'sallys_confession',
- in_reply_to: 'nonexistent_comment',
- message: 'i like you, jack',
- updated: '2015-12-24 15:00:20.396000000',
- }, {
- id: 'sally_to_dr_finklestein',
- in_reply_to: 'nonexistent_comment',
- message: 'i’m running away',
- updated: '2015-10-31 09:00:20.396000000',
- }, {
- id: 'sallys_defiance',
- message: 'i will poison you so i can get away',
- updated: '2015-10-31 15:00:20.396000000',
- }];
- });
-
- test('orphan replies', () => {
- assert.equal(4, element._orderedComments.length);
- });
-
- test('keyboard shortcuts', () => {
- const expandCollapseStub =
- sinon.stub(element, '_expandCollapseComments');
- MockInteractions.pressAndReleaseKeyOn(element, 69, null, 'e');
- assert.isTrue(expandCollapseStub.lastCall.calledWith(false));
-
- MockInteractions.pressAndReleaseKeyOn(element, 69, 'shift', 'e');
- assert.isTrue(expandCollapseStub.lastCall.calledWith(true));
- });
-
- test('comment in_reply_to is either null or most recent comment', () => {
- element._createReplyComment(element.comments[3], 'dummy', true);
- flushAsynchronousOperations();
- assert.equal(element._orderedComments.length, 5);
- assert.equal(element._orderedComments[4].in_reply_to, 'jacks_reply');
- });
-
- test('resolvable comments', () => {
- assert.isFalse(element.unresolved);
- element._createReplyComment(element.comments[3], 'dummy', true, true);
- flushAsynchronousOperations();
- assert.isTrue(element.unresolved);
- });
-
- test('_setInitialExpandedState', () => {
- element.unresolved = true;
- element._setInitialExpandedState();
- for (let i = 0; i < element.comments.length; i++) {
- assert.isFalse(element.comments[i].collapsed);
- }
- element.unresolved = false;
- element._setInitialExpandedState();
- for (let i = 0; i < element.comments.length; i++) {
- assert.isTrue(element.comments[i].collapsed);
- }
- for (let i = 0; i < element.comments.length; i++) {
- element.comments[i].robot_id = 123;
- }
- element._setInitialExpandedState();
- for (let i = 0; i < element.comments.length; i++) {
- assert.isFalse(element.comments[i].collapsed);
- }
- });
- });
-
- test('_computeHostClass', () => {
- assert.equal(element._computeHostClass(true), 'unresolved');
- assert.equal(element._computeHostClass(false), '');
- });
-
- test('addDraft sets unresolved state correctly', () => {
- let unresolved = true;
- element.comments = [];
- element.addDraft(null, null, unresolved);
- assert.equal(element.comments[0].unresolved, true);
-
- unresolved = false; // comment should get added as actually resolved.
- element.comments = [];
- element.addDraft(null, null, unresolved);
- assert.equal(element.comments[0].unresolved, false);
-
- element.comments = [];
- element.addDraft();
- assert.equal(element.comments[0].unresolved, true);
- });
-
- test('_newDraft', () => {
- element.commentSide = 'left';
- element.patchNum = 3;
- const draft = element._newDraft();
- assert.equal(draft.__commentSide, 'left');
- assert.equal(draft.patchNum, 3);
- });
-
- test('new comment gets created', () => {
- element.comments = [];
- element.addOrEditDraft(1);
- assert.equal(element.comments.length, 1);
- // Mock a submitted comment.
- element.comments[0].id = element.comments[0].__draftID;
- element.comments[0].__draft = false;
- element.addOrEditDraft(1);
- assert.equal(element.comments.length, 2);
- });
-
- test('unresolved label', () => {
- element.unresolved = false;
- assert.isTrue(element.$.unresolvedLabel.hasAttribute('hidden'));
- element.unresolved = true;
- assert.isFalse(element.$.unresolvedLabel.hasAttribute('hidden'));
- });
-
- test('draft comments are at the end of orderedComments', () => {
- element.comments = [{
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 2,
- line: 5,
- message: 'Earlier draft',
- updated: '2015-12-08 19:48:33.843000000',
- __draft: true,
- },
- {
- author: {
- name: 'Mr. Peanutbutter2',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 1,
- line: 5,
- message: 'This comment was left last but is not a draft',
- updated: '2015-12-10 19:48:33.843000000',
- },
- {
- author: {
- name: 'Mr. Peanutbutter2',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 3,
- line: 5,
- message: 'Later draft',
- updated: '2015-12-09 19:48:33.843000000',
- __draft: true,
- }];
- assert.equal(element._orderedComments[0].id, '1');
- assert.equal(element._orderedComments[1].id, '2');
- assert.equal(element._orderedComments[2].id, '3');
- });
-
- test('reflects lineNum and commentSide to attributes', () => {
- element.lineNum = 7;
- element.commentSide = 'left';
-
- assert.equal(element.getAttribute('line-num'), '7');
- assert.equal(element.getAttribute('comment-side'), 'left');
- });
-
- test('reflects range to JSON serialized attribute if set', () => {
- element.range = {
- start_line: 4,
- end_line: 5,
- start_character: 6,
- end_character: 7,
- };
-
- assert.deepEqual(
- JSON.parse(element.getAttribute('range')),
- {start_line: 4, end_line: 5, start_character: 6, end_character: 7});
- });
-
- test('removes range attribute if range is unset', () => {
- element.range = {
- start_line: 4,
- end_line: 5,
- start_character: 6,
- end_character: 7,
- };
- element.range = undefined;
-
- assert.notOk(element.hasAttribute('range'));
- });
- });
-</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
deleted file mode 100644
index 8633b0e..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
+++ /dev/null
@@ -1,388 +0,0 @@
-<!--
-@license
-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.
--->
-
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
-<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
-<link rel="import" href="../../../styles/shared-styles.html">
-<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
-<link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
-<link rel="import" href="../../plugins/gr-endpoint-param/gr-endpoint-param.html">
-<link rel="import" href="../../shared/gr-button/gr-button.html">
-<link rel="import" href="../../shared/gr-dialog/gr-dialog.html">
-<link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
-<link rel="import" href="../../shared/gr-formatted-text/gr-formatted-text.html">
-<link rel="import" href="../../shared/gr-icons/gr-icons.html">
-<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
-<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
-<link rel="import" href="../../shared/gr-storage/gr-storage.html">
-<link rel="import" href="../../shared/gr-textarea/gr-textarea.html">
-<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
-<link rel="import" href="../gr-confirm-delete-comment-dialog/gr-confirm-delete-comment-dialog.html">
-<script src="../../../scripts/rootElement.js"></script>
-
-<dom-module id="gr-diff-comment">
- <template>
- <style include="shared-styles">
- :host {
- display: block;
- font-family: var(--font-family);
- padding: .7em .7em;
- --iron-autogrow-textarea: {
- box-sizing: border-box;
- padding: 2px;
- };
- }
- :host([disabled]) {
- pointer-events: none;
- }
- :host([disabled]) .actions,
- :host([disabled]) .robotActions,
- :host([disabled]) .date {
- opacity: .5;
- }
- :host([discarding]) {
- display: none;
- }
- .header {
- align-items: baseline;
- cursor: pointer;
- display: flex;
- font-family: 'Open Sans', sans-serif;
- margin: -.7em -.7em 0 -.7em;
- padding: .7em;
- }
- .container.collapsed .header {
- margin-bottom: -.7em;
- }
- .headerMiddle {
- color: var(--deemphasized-text-color);
- flex: 1;
- overflow: hidden;
- }
- .authorName,
- .draftLabel,
- .draftTooltip {
- font-weight: var(--font-weight-bold);
- }
- .draftLabel,
- .draftTooltip {
- color: var(--deemphasized-text-color);
- display: none;
- }
- .date {
- justify-content: flex-end;
- margin-left: 5px;
- min-width: 4.5em;
- text-align: right;
- white-space: nowrap;
- }
- span.date {
- color: var(--deemphasized-text-color);
- }
- span.date:hover {
- text-decoration: underline;
- }
- .actions {
- display: flex;
- justify-content: flex-end;
- padding-top: 0;
- }
- .action {
- margin-left: 1em;
- }
- .robotActions {
- display: flex;
- justify-content: flex-start;
- padding-top: 0;
- }
- .robotActions .action {
- /* Keep button text lined up with output text */
- margin-left: -.3rem;
- margin-right: 1em;
- }
- .rightActions {
- display: flex;
- justify-content: flex-end;
- }
- .editMessage {
- display: none;
- margin: .5em 0;
- width: 100%;
- }
- .container:not(.draft) .actions .hideOnPublished {
- display: none;
- }
- .draft .reply,
- .draft .quote,
- .draft .ack,
- .draft .done {
- display: none;
- }
- .draft .draftLabel,
- .draft .draftTooltip {
- display: inline;
- }
- .draft:not(.editing) .save,
- .draft:not(.editing) .cancel {
- display: none;
- }
- .editing .message,
- .editing .reply,
- .editing .quote,
- .editing .ack,
- .editing .done,
- .editing .edit,
- .editing .discard,
- .editing .unresolved {
- display: none;
- }
- .editing .editMessage {
- display: block;
- }
- .show-hide {
- margin-left: .4em;
- }
- .robotId {
- color: var(--deemphasized-text-color);
- margin-bottom: .8em;
- margin-top: -.4em;
- }
- .robotIcon {
- margin-right: .2em;
- /* because of the antenna of the robot, it looks off center even when it
- is centered. artificially adjust margin to account for this. */
- margin-top: -.3em;
- }
- .runIdInformation {
- margin: .7em 0;
- }
- .robotRun {
- margin-left: .5em;
- }
- .robotRunLink {
- margin-left: .5em;
- }
- input.show-hide {
- display: none;
- }
- label.show-hide {
- color: var(--comment-text-color);
- cursor: pointer;
- display: block;
- font-size: .8rem;
- height: 1.1em;
- margin-top: .1em;
- }
- #container .collapsedContent {
- display: none;
- }
- #container.collapsed {
- padding-bottom: 3px;
- }
- #container.collapsed .collapsedContent {
- display: block;
- overflow: hidden;
- padding-left: 5px;
- text-overflow: ellipsis;
- white-space: nowrap;
- }
- #container.collapsed .actions,
- #container.collapsed gr-formatted-text,
- #container.collapsed gr-textarea {
- display: none;
- }
- .resolve,
- .unresolved {
- align-items: center;
- display: flex;
- flex: 1;
- margin: 0;
- }
- .resolve label {
- color: var(--comment-text-color);
- }
- gr-dialog .main {
- display: flex;
- flex-direction: column;
- width: 100%;
- }
- #deleteBtn {
- display: none;
- --gr-button: {
- color: var(--deemphasized-text-color);
- padding: 0;
- }
- }
- #deleteBtn.showDeleteButtons {
- display: block;
- }
- </style>
- <div id="container" class="container">
- <div class="header" id="header" on-tap="_handleToggleCollapsed">
- <div class="headerLeft">
- <span class="authorName">[[comment.author.name]]</span>
- <span class="draftLabel">DRAFT</span>
- <gr-tooltip-content class="draftTooltip"
- has-tooltip
- title="This draft is only visible to you. To publish drafts, click the 'Reply' or 'Start review' button at the top of the change or press the 'A' key."
- max-width="20em"
- show-icon></gr-tooltip-content>
- </div>
- <div class="headerMiddle">
- <span class="collapsedContent">[[comment.message]]</span>
- </div>
- <gr-button
- id="deleteBtn"
- link
- secondary
- class$="action delete [[_computeDeleteButtonClass(_isAdmin, draft)]]"
- on-tap="_handleCommentDelete">
- (Delete)
- </gr-button>
- <span class="date" on-tap="_handleAnchorTap">
- <gr-date-formatter
- has-tooltip
- date-str="[[comment.updated]]"></gr-date-formatter>
- </span>
- <div class="show-hide">
- <label class="show-hide">
- <input type="checkbox" class="show-hide"
- checked$="[[collapsed]]"
- on-change="_handleToggleCollapsed">
- [[_computeShowHideText(collapsed)]]
- </label>
- </div>
- </div>
- <div class="body">
- <template is="dom-if" if="[[comment.robot_id]]">
- <div class="robotId" hidden$="[[collapsed]]">
- <iron-icon class="robotIcon" icon="gr-icons:robot"></iron-icon>
- [[comment.robot_id]]
- </div>
- </template>
- <template is="dom-if" if="[[editing]]">
- <gr-textarea
- id="editTextarea"
- class="editMessage"
- autocomplete="on"
- monospace
- disabled="{{disabled}}"
- rows="4"
- text="{{_messageText}}"></gr-textarea>
- </template>
- <!--The message class is needed to ensure selectability from
- gr-diff-selection.-->
- <gr-formatted-text class="message"
- content="[[comment.message]]"
- no-trailing-margin="[[!comment.__draft]]"
- collapsed="[[collapsed]]"
- config="[[projectConfig.commentlinks]]"></gr-formatted-text>
- <div hidden$="[[!comment.robot_run_id]]" class="message">
- <div class="runIdInformation" hidden$="[[collapsed]]">
- Run ID:
- <template is="dom-if" if="[[comment.url]]">
- <a class="robotRunLink" href$="[[comment.url]]">
- <span class="robotRun link">[[comment.robot_run_id]]</span>
- </a>
- </template>
- <template is="dom-if" if="[[!comment.url]]">
- <span class="robotRun text">[[comment.robot_run_id]]</span>
- </template>
- </div>
- </div>
- <div class="actions humanActions" hidden$="[[!_showHumanActions]]">
- <div class="action resolve hideOnPublished">
- <label>
- <input type="checkbox"
- id="resolvedCheckbox"
- checked="[[resolved]]"
- on-change="_handleToggleResolved">
- Resolved
- </label>
- </div>
- <div class="rightActions">
- <gr-button
- link
- secondary
- class="action cancel hideOnPublished"
- on-tap="_handleCancel">Cancel</gr-button>
- <gr-button
- link
- secondary
- class="action discard hideOnPublished"
- on-tap="_handleDiscard">Discard</gr-button>
- <gr-button
- link
- secondary
- class="action edit hideOnPublished"
- on-tap="_handleEdit">Edit</gr-button>
- <gr-button
- link
- secondary
- disabled$="[[_computeSaveDisabled(_messageText, comment, resolved)]]"
- class="action save hideOnPublished"
- on-tap="_handleSave">Save</gr-button>
- </div>
- </div>
- <div class="robotActions" hidden$="[[!_showRobotActions]]">
- <template is="dom-if" if="[[isRobotComment]]">
- <gr-button
- link
- secondary
- class="action fix"
- on-tap="_handleFix"
- disabled="[[robotButtonDisabled]]">
- Please Fix
- </gr-button>
- <gr-endpoint-decorator name="robot-comment-controls">
- <gr-endpoint-param name="comment" value="[[comment]]">
- </gr-endpoint-param>
- </gr-endpoint-decorator>
- </template>
- </div>
- </div>
- </div>
- <template is="dom-if" if="[[_enableOverlay]]">
- <gr-overlay id="confirmDeleteOverlay" with-backdrop>
- <gr-confirm-delete-comment-dialog id="confirmDeleteComment"
- on-confirm="_handleConfirmDeleteComment"
- on-cancel="_handleCancelDeleteComment">
- </gr-confirm-delete-comment-dialog>
- </gr-overlay>
- <gr-overlay id="confirmDiscardOverlay" with-backdrop>
- <gr-dialog
- id="confirmDiscardDialog"
- confirm-label="Discard"
- confirm-on-enter
- on-confirm="_handleConfirmDiscard"
- on-cancel="_closeConfirmDiscardOverlay">
- <div class="header" slot="header">
- Discard comment
- </div>
- <div class="main" slot="main">
- Are you sure you want to discard this draft comment?
- </div>
- </gr-dialog>
- </gr-overlay>
- </template>
- <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
- <gr-storage id="storage"></gr-storage>
- <gr-reporting id="reporting"></gr-reporting>
- </template>
- <script src="gr-diff-comment.js"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
deleted file mode 100644
index a46a0b2..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.js
+++ /dev/null
@@ -1,648 +0,0 @@
-/**
- * @license
- * 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.
- */
-(function() {
- 'use strict';
-
- const STORAGE_DEBOUNCE_INTERVAL = 400;
- const TOAST_DEBOUNCE_INTERVAL = 200;
-
- const SAVING_MESSAGE = 'Saving';
- const DRAFT_SINGULAR = 'draft...';
- const DRAFT_PLURAL = 'drafts...';
- const SAVED_MESSAGE = 'All changes saved';
-
- const REPORT_CREATE_DRAFT = 'CreateDraftComment';
- const REPORT_UPDATE_DRAFT = 'UpdateDraftComment';
- const REPORT_DISCARD_DRAFT = 'DiscardDraftComment';
-
- const FILE = 'FILE';
-
- Polymer({
- is: 'gr-diff-comment',
-
- /**
- * Fired when the create fix comment action is triggered.
- *
- * @event create-fix-comment
- */
-
- /**
- * Fired when this comment is discarded.
- *
- * @event comment-discard
- */
-
- /**
- * Fired when this comment is saved.
- *
- * @event comment-save
- */
-
- /**
- * Fired when this comment is updated.
- *
- * @event comment-update
- */
-
- /**
- * Fired when the comment's timestamp is tapped.
- *
- * @event comment-anchor-tap
- */
-
- properties: {
- changeNum: String,
- /** @type {?} */
- comment: {
- type: Object,
- notify: true,
- observer: '_commentChanged',
- },
- isRobotComment: {
- type: Boolean,
- value: false,
- reflectToAttribute: true,
- },
- disabled: {
- type: Boolean,
- value: false,
- reflectToAttribute: true,
- },
- draft: {
- type: Boolean,
- value: false,
- observer: '_draftChanged',
- },
- editing: {
- type: Boolean,
- value: false,
- observer: '_editingChanged',
- },
- discarding: {
- type: Boolean,
- value: false,
- reflectToAttribute: true,
- },
- hasChildren: Boolean,
- patchNum: String,
- showActions: Boolean,
- _showHumanActions: Boolean,
- _showRobotActions: Boolean,
- collapsed: {
- type: Boolean,
- value: true,
- observer: '_toggleCollapseClass',
- },
- /** @type {?} */
- projectConfig: Object,
- robotButtonDisabled: Boolean,
- _isAdmin: {
- type: Boolean,
- value: false,
- },
-
- _xhrPromise: Object, // Used for testing.
- _messageText: {
- type: String,
- value: '',
- observer: '_messageTextChanged',
- },
- commentSide: String,
-
- resolved: Boolean,
-
- _numPendingDraftRequests: {
- type: Object,
- value: {number: 0}, // Intentional to share the object across instances.
- },
-
- _enableOverlay: {
- type: Boolean,
- value: false,
- },
-
- /**
- * Property for storing references to overlay elements. When the overlays
- * are moved to Gerrit.getRootElement() to be shown they are no-longer
- * children, so they can't be queried along the tree, so they are stored
- * here.
- */
- _overlays: {
- type: Object,
- value: () => ({}),
- },
- },
-
- observers: [
- '_commentMessageChanged(comment.message)',
- '_loadLocalDraft(changeNum, patchNum, comment)',
- '_isRobotComment(comment)',
- '_calculateActionstoShow(showActions, isRobotComment)',
- ],
-
- behaviors: [
- Gerrit.KeyboardShortcutBehavior,
- ],
-
- keyBindings: {
- 'ctrl+enter meta+enter ctrl+s meta+s': '_handleSaveKey',
- 'esc': '_handleEsc',
- },
-
- attached() {
- if (this.editing) {
- this.collapsed = false;
- } else if (this.comment) {
- this.collapsed = this.comment.collapsed;
- }
- this._getIsAdmin().then(isAdmin => {
- this._isAdmin = isAdmin;
- });
- },
-
- detached() {
- this.cancelDebouncer('fire-update');
- if (this.textarea) {
- this.textarea.closeDropdown();
- }
- },
-
- get textarea() {
- return this.$$('#editTextarea');
- },
-
- get confirmDeleteOverlay() {
- if (!this._overlays.confirmDelete) {
- this._enableOverlay = true;
- Polymer.dom.flush();
- this._overlays.confirmDelete = this.$$('#confirmDeleteOverlay');
- }
- return this._overlays.confirmDelete;
- },
-
- get confirmDiscardOverlay() {
- if (!this._overlays.confirmDiscard) {
- this._enableOverlay = true;
- Polymer.dom.flush();
- this._overlays.confirmDiscard = this.$$('#confirmDiscardOverlay');
- }
- return this._overlays.confirmDiscard;
- },
-
- _computeShowHideText(collapsed) {
- return collapsed ? 'â—€' : 'â–¼';
- },
-
- _calculateActionstoShow(showActions, isRobotComment) {
- this._showHumanActions = showActions && !isRobotComment;
- this._showRobotActions = showActions && isRobotComment;
- },
-
- _isRobotComment(comment) {
- this.isRobotComment = !!comment.robot_id;
- },
-
- isOnParent() {
- return this.side === 'PARENT';
- },
-
- _getIsAdmin() {
- return this.$.restAPI.getIsAdmin();
- },
-
- /**
- * @param {*=} opt_comment
- */
- save(opt_comment) {
- let comment = opt_comment;
- if (!comment) { comment = this.comment; }
-
- this.set('comment.message', this._messageText);
- this.editing = false;
- this.disabled = true;
-
- if (!this._messageText) {
- return this._discardDraft();
- }
-
- this._xhrPromise = this._saveDraft(comment).then(response => {
- this.disabled = false;
- if (!response.ok) { return response; }
-
- this._eraseDraftComment();
- return this.$.restAPI.getResponseObject(response).then(obj => {
- const resComment = obj;
- resComment.__draft = true;
- // Maintain the ephemeral draft ID for identification by other
- // elements.
- if (this.comment.__draftID) {
- resComment.__draftID = this.comment.__draftID;
- }
- resComment.__commentSide = this.commentSide;
- this.comment = resComment;
- this._fireSave();
- return obj;
- });
- }).catch(err => {
- this.disabled = false;
- throw err;
- });
-
- return this._xhrPromise;
- },
-
- _eraseDraftComment() {
- // Prevents a race condition in which removing the draft comment occurs
- // prior to it being saved.
- this.cancelDebouncer('store');
-
- this.$.storage.eraseDraftComment({
- changeNum: this.changeNum,
- patchNum: this._getPatchNum(),
- path: this.comment.path,
- line: this.comment.line,
- range: this.comment.range,
- });
- },
-
- _commentChanged(comment) {
- this.editing = !!comment.__editing;
- this.resolved = !comment.unresolved;
- if (this.editing) { // It's a new draft/reply, notify.
- this._fireUpdate();
- }
- },
-
- /**
- * @param {!Object=} opt_mixin
- *
- * @return {!Object}
- */
- _getEventPayload(opt_mixin) {
- return Object.assign({}, opt_mixin, {
- comment: this.comment,
- patchNum: this.patchNum,
- });
- },
-
- _fireSave() {
- this.fire('comment-save', this._getEventPayload());
- },
-
- _fireUpdate() {
- this.debounce('fire-update', () => {
- this.fire('comment-update', this._getEventPayload());
- });
- },
-
- _draftChanged(draft) {
- this.$.container.classList.toggle('draft', draft);
- },
-
- _editingChanged(editing, previousValue) {
- this.$.container.classList.toggle('editing', editing);
- if (this.comment && this.comment.id) {
- this.$$('.cancel').hidden = !editing;
- }
- if (this.comment) {
- this.comment.__editing = this.editing;
- }
- if (editing != !!previousValue) {
- // To prevent event firing on comment creation.
- this._fireUpdate();
- }
- if (editing) {
- this.async(() => {
- Polymer.dom.flush();
- this.textarea.putCursorAtEnd();
- }, 1);
- }
- },
-
- _computeDeleteButtonClass(isAdmin, draft) {
- return isAdmin && !draft ? 'showDeleteButtons' : '';
- },
-
- _computeSaveDisabled(draft, comment, resolved) {
- // If resolved state has changed and a msg exists, save should be enabled.
- if (comment.unresolved === resolved && draft) { return false; }
- return !draft || draft.trim() === '';
- },
-
- _handleSaveKey(e) {
- if (!this._computeSaveDisabled(this._messageText, this.comment,
- this.resolved)) {
- e.preventDefault();
- this._handleSave(e);
- }
- },
-
- _handleEsc(e) {
- if (!this._messageText.length) {
- e.preventDefault();
- this._handleCancel(e);
- }
- },
-
- _handleToggleCollapsed() {
- this.collapsed = !this.collapsed;
- },
-
- _toggleCollapseClass(collapsed) {
- if (collapsed) {
- this.$.container.classList.add('collapsed');
- } else {
- this.$.container.classList.remove('collapsed');
- }
- },
-
- _commentMessageChanged(message) {
- this._messageText = message || '';
- },
-
- _messageTextChanged(newValue, oldValue) {
- if (!this.comment || (this.comment && this.comment.id)) { return; }
-
- this.debounce('store', () => {
- const message = this._messageText;
- const commentLocation = {
- changeNum: this.changeNum,
- patchNum: this._getPatchNum(),
- path: this.comment.path,
- line: this.comment.line,
- range: this.comment.range,
- };
-
- if ((!this._messageText || !this._messageText.length) && oldValue) {
- // If the draft has been modified to be empty, then erase the storage
- // entry.
- this.$.storage.eraseDraftComment(commentLocation);
- } else {
- this.$.storage.setDraftComment(commentLocation, message);
- }
- }, STORAGE_DEBOUNCE_INTERVAL);
- },
-
- _handleAnchorTap(e) {
- e.preventDefault();
- if (!this.comment.line) { return; }
- this.dispatchEvent(new CustomEvent('comment-anchor-tap', {
- bubbles: true,
- detail: {
- number: this.comment.line || FILE,
- side: this.side,
- },
- }));
- },
-
- _handleEdit(e) {
- e.preventDefault();
- this._messageText = this.comment.message;
- this.editing = true;
- this.$.reporting.recordDraftInteraction();
- },
-
- _handleSave(e) {
- e.preventDefault();
-
- // Ignore saves started while already saving.
- if (this.disabled) { return; }
- const timingLabel = this.comment.id ?
- REPORT_UPDATE_DRAFT : REPORT_CREATE_DRAFT;
- const timer = this.$.reporting.getTimer(timingLabel);
- this.set('comment.__editing', false);
- return this.save().then(() => { timer.end(); });
- },
-
- _handleCancel(e) {
- e.preventDefault();
-
- if (!this.comment.message ||
- this.comment.message.trim().length === 0 ||
- !this.comment.id) {
- this._fireDiscard();
- return;
- }
- this._messageText = this.comment.message;
- this.editing = false;
- },
-
- _fireDiscard() {
- this.cancelDebouncer('fire-update');
- this.fire('comment-discard', this._getEventPayload());
- },
-
- _handleFix() {
- this.dispatchEvent(new CustomEvent('create-fix-comment', {
- bubbles: true,
- detail: this._getEventPayload(),
- }));
- },
-
- _handleDiscard(e) {
- e.preventDefault();
- this.$.reporting.recordDraftInteraction();
-
- if (!this._messageText) {
- this._discardDraft();
- return;
- }
-
- this._openOverlay(this.confirmDiscardOverlay).then(() => {
- this.confirmDiscardOverlay.querySelector('#confirmDiscardDialog')
- .resetFocus();
- });
- },
-
- _handleConfirmDiscard(e) {
- e.preventDefault();
- const timer = this.$.reporting.getTimer(REPORT_DISCARD_DRAFT);
- this._closeConfirmDiscardOverlay();
- return this._discardDraft().then(() => { timer.end(); });
- },
-
- _discardDraft() {
- if (!this.comment.__draft) {
- throw Error('Cannot discard a non-draft comment.');
- }
- this.discarding = true;
- this.editing = false;
- this.disabled = true;
- this._eraseDraftComment();
-
- if (!this.comment.id) {
- this.disabled = false;
- this._fireDiscard();
- return;
- }
-
- this._xhrPromise = this._deleteDraft(this.comment).then(response => {
- this.disabled = false;
- if (!response.ok) {
- this.discarding = false;
- return response;
- }
-
- this._fireDiscard();
- }).catch(err => {
- this.disabled = false;
- throw err;
- });
-
- return this._xhrPromise;
- },
-
- _closeConfirmDiscardOverlay() {
- this._closeOverlay(this.confirmDiscardOverlay);
- },
-
- _getSavingMessage(numPending) {
- if (numPending === 0) { return SAVED_MESSAGE; }
- return [
- SAVING_MESSAGE,
- numPending,
- numPending === 1 ? DRAFT_SINGULAR : DRAFT_PLURAL,
- ].join(' ');
- },
-
- _showStartRequest() {
- const numPending = ++this._numPendingDraftRequests.number;
- this._updateRequestToast(numPending);
- },
-
- _showEndRequest() {
- const numPending = --this._numPendingDraftRequests.number;
- this._updateRequestToast(numPending);
- },
-
- _handleFailedDraftRequest() {
- this._numPendingDraftRequests.number--;
-
- // Cancel the debouncer so that error toasts from the error-manager will
- // not be overridden.
- this.cancelDebouncer('draft-toast');
- },
-
- _updateRequestToast(numPending) {
- const message = this._getSavingMessage(numPending);
- this.debounce('draft-toast', () => {
- // Note: the event is fired on the body rather than this element because
- // this element may not be attached by the time this executes, in which
- // case the event would not bubble.
- document.body.dispatchEvent(new CustomEvent('show-alert',
- {detail: {message}, bubbles: true}));
- }, TOAST_DEBOUNCE_INTERVAL);
- },
-
- _saveDraft(draft) {
- this._showStartRequest();
- return this.$.restAPI.saveDiffDraft(this.changeNum, this.patchNum, draft)
- .then(result => {
- if (result.ok) {
- this._showEndRequest();
- } else {
- this._handleFailedDraftRequest();
- }
- return result;
- });
- },
-
- _deleteDraft(draft) {
- this._showStartRequest();
- return this.$.restAPI.deleteDiffDraft(this.changeNum, this.patchNum,
- draft).then(result => {
- if (result.ok) {
- this._showEndRequest();
- } else {
- this._handleFailedDraftRequest();
- }
- return result;
- });
- },
-
- _getPatchNum() {
- return this.isOnParent() ? 'PARENT' : this.patchNum;
- },
-
- _loadLocalDraft(changeNum, patchNum, comment) {
- // Only apply local drafts to comments that haven't been saved
- // remotely, and haven't been given a default message already.
- //
- // Don't get local draft if there is another comment that is currently
- // in an editing state.
- if (!comment || comment.id || comment.message || comment.__otherEditing) {
- delete comment.__otherEditing;
- return;
- }
-
- const draft = this.$.storage.getDraftComment({
- changeNum,
- patchNum: this._getPatchNum(),
- path: comment.path,
- line: comment.line,
- range: comment.range,
- });
-
- if (draft) {
- this.set('comment.message', draft.message);
- }
- },
-
- _handleToggleResolved() {
- this.$.reporting.recordDraftInteraction();
- this.resolved = !this.resolved;
- // Modify payload instead of this.comment, as this.comment is passed from
- // the parent by ref.
- const payload = this._getEventPayload();
- payload.comment.unresolved = !this.$.resolvedCheckbox.checked;
- this.fire('comment-update', payload);
- if (!this.editing) {
- // Save the resolved state immediately.
- this.save(payload.comment);
- }
- },
-
- _handleCommentDelete() {
- this._openOverlay(this.confirmDeleteOverlay);
- },
-
- _handleCancelDeleteComment() {
- this._closeOverlay(this.confirmDeleteOverlay);
- },
-
- _openOverlay(overlay) {
- Polymer.dom(Gerrit.getRootElement()).appendChild(overlay);
- return overlay.open();
- },
-
- _closeOverlay(overlay) {
- Polymer.dom(Gerrit.getRootElement()).removeChild(overlay);
- overlay.close();
- },
-
- _handleConfirmDeleteComment() {
- const dialog =
- this.confirmDeleteOverlay.querySelector('#confirmDeleteComment');
- this.$.restAPI.deleteComment(
- this.changeNum, this.patchNum, this.comment.id, dialog.message)
- .then(newComment => {
- this._handleCancelDeleteComment();
- this.comment = newComment;
- });
- },
- });
-})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html
deleted file mode 100644
index 912e615..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment_test.html
+++ /dev/null
@@ -1,847 +0,0 @@
-<!DOCTYPE html>
-<!--
-@license
-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-comment</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>
-<script src="../../../scripts/util.js"></script>
-
-<link rel="import" href="gr-diff-comment.html">
-
-<script>void(0);</script>
-
-<test-fixture id="basic">
- <template>
- <gr-diff-comment></gr-diff-comment>
- </template>
-</test-fixture>
-
-<test-fixture id="draft">
- <template>
- <gr-diff-comment draft="true"></gr-diff-comment>
- </template>
-</test-fixture>
-
-<script>
-
- function isVisible(el) {
- assert.ok(el);
- return getComputedStyle(el).getPropertyValue('display') !== 'none';
- }
-
- suite('gr-diff-comment tests', () => {
- let element;
- let sandbox;
- setup(() => {
- stub('gr-rest-api-interface', {
- getAccount() { return Promise.resolve(null); },
- });
- element = fixture('basic');
- element.comment = {
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- id: 'baf0414d_60047215',
- line: 5,
- message: 'is this a crossover episode!?',
- updated: '2015-12-08 19:48:33.843000000',
- };
- sandbox = sinon.sandbox.create();
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('collapsible comments', () => {
- // When a comment (not draft) is loaded, it should be collapsed
- assert.isTrue(element.collapsed);
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isFalse(isVisible(element.$$('.actions')),
- 'actions are not visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
-
- // The header middle content is only visible when comments are collapsed.
- // It shows the message in a condensed way, and limits to a single line.
- assert.isTrue(isVisible(element.$$('.collapsedContent')),
- 'header middle content is visible');
-
- // When the header row is clicked, the comment should expand
- MockInteractions.tap(element.$.header);
- assert.isFalse(element.collapsed);
- assert.isTrue(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is visible');
- assert.isTrue(isVisible(element.$$('.actions')),
- 'actions are visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
- assert.isFalse(isVisible(element.$$('.collapsedContent')),
- 'header middle content is not visible');
- });
-
- test('clicking on date link fires event', () => {
- element.side = 'PARENT';
- const stub = sinon.stub();
- element.addEventListener('comment-anchor-tap', stub);
- const dateEl = element.$$('.date');
- assert.ok(dateEl);
- MockInteractions.tap(dateEl);
-
- assert.isTrue(stub.called);
- assert.deepEqual(stub.lastCall.args[0].detail,
- {side: element.side, number: element.comment.line});
- });
-
- test('message is not retrieved from storage when other edits', done => {
- const storageStub = sandbox.stub(element.$.storage, 'getDraftComment');
- const loadSpy = sandbox.spy(element, '_loadLocalDraft');
-
- element.changeNum = 1;
- element.patchNum = 1;
- element.comment = {
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- line: 5,
- __otherEditing: true,
- };
- flush(() => {
- assert.isTrue(loadSpy.called);
- assert.isFalse(storageStub.called);
- done();
- });
- });
-
- test('message is retrieved from storage when no other edits', done => {
- const storageStub = sandbox.stub(element.$.storage, 'getDraftComment');
- const loadSpy = sandbox.spy(element, '_loadLocalDraft');
-
- element.changeNum = 1;
- element.patchNum = 1;
- element.comment = {
- author: {
- name: 'Mr. Peanutbutter',
- email: 'tenn1sballchaser@aol.com',
- },
- line: 5,
- };
- flush(() => {
- assert.isTrue(loadSpy.called);
- assert.isTrue(storageStub.called);
- done();
- });
- });
-
- test('_getPatchNum', () => {
- element.side = 'PARENT';
- element.patchNum = 1;
- assert.equal(element._getPatchNum(), 'PARENT');
- element.side = 'REVISION';
- assert.equal(element._getPatchNum(), 1);
- });
-
- test('comment expand and collapse', () => {
- element.collapsed = true;
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isFalse(isVisible(element.$$('.actions')),
- 'actions are not visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
- assert.isTrue(isVisible(element.$$('.collapsedContent')),
- 'header middle content is visible');
-
- element.collapsed = false;
- assert.isFalse(element.collapsed);
- assert.isTrue(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is visible');
- assert.isTrue(isVisible(element.$$('.actions')),
- 'actions are visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
- assert.isFalse(isVisible(element.$$('.collapsedContent')),
- 'header middle content is is not visible');
- });
-
- suite('while editing', () => {
- setup(() => {
- element.editing = true;
- element._messageText = 'test';
- sandbox.stub(element, '_handleCancel');
- sandbox.stub(element, '_handleSave');
- flushAsynchronousOperations();
- });
-
- suite('when text is empty', () => {
- setup(() => {
- element._messageText = '';
- element.comment = {};
- });
-
- test('esc closes comment when text is empty', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 27); // esc
- assert.isTrue(element._handleCancel.called);
- });
-
- test('ctrl+enter does not save', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 13, 'ctrl'); // ctrl + enter
- assert.isFalse(element._handleSave.called);
- });
-
- test('meta+enter does not save', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 13, 'meta'); // meta + enter
- assert.isFalse(element._handleSave.called);
- });
-
- test('ctrl+s does not save', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 83, 'ctrl'); // ctrl + s
- assert.isFalse(element._handleSave.called);
- });
- });
-
- test('esc does not close comment that has content', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 27); // esc
- assert.isFalse(element._handleCancel.called);
- });
-
- test('ctrl+enter saves', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 13, 'ctrl'); // ctrl + enter
- assert.isTrue(element._handleSave.called);
- });
-
- test('meta+enter saves', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 13, 'meta'); // meta + enter
- assert.isTrue(element._handleSave.called);
- });
-
- test('ctrl+s saves', () => {
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea, 83, 'ctrl'); // ctrl + s
- assert.isTrue(element._handleSave.called);
- });
- });
- test('delete comment button for non-admins is hidden', () => {
- element._isAdmin = false;
- assert.isFalse(element.$$('.action.delete')
- .classList.contains('showDeleteButtons'));
- });
-
- test('delete comment button for admins with draft is hidden', () => {
- element._isAdmin = false;
- element.draft = true;
- assert.isFalse(element.$$('.action.delete')
- .classList.contains('showDeleteButtons'));
- });
-
- test('delete comment', done => {
- sandbox.stub(
- element.$.restAPI, 'deleteComment').returns(Promise.resolve({}));
- sandbox.spy(element.confirmDeleteOverlay, 'open');
- element.changeNum = 42;
- element.patchNum = 0xDEADBEEF;
- element._isAdmin = true;
- assert.isTrue(element.$$('.action.delete')
- .classList.contains('showDeleteButtons'));
- MockInteractions.tap(element.$$('.action.delete'));
- flush(() => {
- element.confirmDeleteOverlay.open.lastCall.returnValue.then(() => {
- const dialog =
- this.confirmDeleteOverlay.querySelector('#confirmDeleteComment');
- dialog.message = 'removal reason';
- element._handleConfirmDeleteComment();
- assert.isTrue(element.$.restAPI.deleteComment.calledWith(
- 42, 0xDEADBEEF, 'baf0414d_60047215', 'removal reason'));
- done();
- });
- });
- });
-
- suite('draft update reporting', () => {
- let endStub;
- let getTimerStub;
- let mockEvent;
-
- setup(() => {
- mockEvent = {preventDefault() {}};
- sandbox.stub(element, 'save')
- .returns(Promise.resolve({}));
- sandbox.stub(element, '_discardDraft')
- .returns(Promise.resolve({}));
- endStub = sinon.stub();
- getTimerStub = sandbox.stub(element.$.reporting, 'getTimer')
- .returns({end: endStub});
- });
-
- test('create', () => {
- element.comment = {};
- return element._handleSave(mockEvent).then(() => {
- assert.isTrue(endStub.calledOnce);
- assert.isTrue(getTimerStub.calledOnce);
- assert.equal(getTimerStub.lastCall.args[0], 'CreateDraftComment');
- });
- });
-
- test('update', () => {
- element.comment = {id: 'abc_123'};
- return element._handleSave(mockEvent).then(() => {
- assert.isTrue(endStub.calledOnce);
- assert.isTrue(getTimerStub.calledOnce);
- assert.equal(getTimerStub.lastCall.args[0], 'UpdateDraftComment');
- });
- });
-
- test('discard', () => {
- element.comment = {id: 'abc_123'};
- sandbox.stub(element, '_closeConfirmDiscardOverlay');
- return element._handleConfirmDiscard(mockEvent).then(() => {
- assert.isTrue(endStub.calledOnce);
- assert.isTrue(getTimerStub.calledOnce);
- assert.equal(getTimerStub.lastCall.args[0], 'DiscardDraftComment');
- });
- });
- });
-
- test('edit reports interaction', () => {
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- MockInteractions.tap(element.$$('.edit'));
- assert.isTrue(reportStub.calledOnce);
- });
-
- test('discard reports interaction', () => {
- const reportStub = sandbox.stub(element.$.reporting,
- 'recordDraftInteraction');
- element.draft = true;
- MockInteractions.tap(element.$$('.discard'));
- assert.isTrue(reportStub.calledOnce);
- });
- });
-
- suite('gr-diff-comment draft tests', () => {
- let element;
- let sandbox;
-
- setup(() => {
- stub('gr-rest-api-interface', {
- getAccount() { return Promise.resolve(null); },
- saveDiffDraft() {
- return Promise.resolve({
- ok: true,
- text() {
- return Promise.resolve(
- ')]}\'\n{' +
- '"id": "baf0414d_40572e03",' +
- '"path": "/path/to/file",' +
- '"line": 5,' +
- '"updated": "2015-12-08 21:52:36.177000000",' +
- '"message": "saved!"' +
- '}'
- );
- },
- });
- },
- removeChangeReviewer() {
- return Promise.resolve({ok: true});
- },
- });
- stub('gr-storage', {
- getDraftComment() { return null; },
- });
- element = fixture('draft');
- element.changeNum = 42;
- element.patchNum = 1;
- element.editing = false;
- element.comment = {
- __commentSide: 'right',
- __draft: true,
- __draftID: 'temp_draft_id',
- path: '/path/to/file',
- line: 5,
- };
- element.commentSide = 'right';
- sandbox = sinon.sandbox.create();
- });
-
- teardown(() => {
- sandbox.restore();
- });
-
- test('button visibility states', () => {
- element.showActions = false;
- assert.isTrue(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.showActions = true;
- assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.draft = true;
- assert.isTrue(isVisible(element.$$('.edit')), 'edit is visible');
- assert.isTrue(isVisible(element.$$('.discard')), 'discard is visible');
- assert.isFalse(isVisible(element.$$('.save')), 'save is not visible');
- assert.isFalse(isVisible(element.$$('.cancel')), 'cancel is not visible');
- assert.isTrue(isVisible(element.$$('.resolve')), 'resolve is visible');
- assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.editing = true;
- flushAsynchronousOperations();
- assert.isFalse(isVisible(element.$$('.edit')), 'edit is not visible');
- assert.isFalse(isVisible(element.$$('.discard')), 'discard not visible');
- assert.isTrue(isVisible(element.$$('.save')), 'save is visible');
- assert.isTrue(isVisible(element.$$('.cancel')), 'cancel is visible');
- assert.isTrue(isVisible(element.$$('.resolve')), 'resolve is visible');
- assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.draft = false;
- element.editing = false;
- flushAsynchronousOperations();
- assert.isFalse(isVisible(element.$$('.edit')), 'edit is not visible');
- assert.isFalse(isVisible(element.$$('.discard')),
- 'discard is not visible');
- assert.isFalse(isVisible(element.$$('.save')), 'save is not visible');
- assert.isFalse(isVisible(element.$$('.cancel')), 'cancel is not visible');
- assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.comment.id = 'foo';
- element.draft = true;
- element.editing = true;
- flushAsynchronousOperations();
- assert.isTrue(isVisible(element.$$('.cancel')), 'cancel is visible');
- assert.isFalse(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isTrue(element.$$('.robotActions').hasAttribute('hidden'));
-
- element.isRobotComment = true;
- element.draft = true;
- assert.isTrue(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isFalse(element.$$('.robotActions').hasAttribute('hidden'));
-
- // It is not expected to see Robot comment drafts, but if they appear,
- // they will behave the same as non-drafts.
- element.draft = false;
- assert.isTrue(element.$$('.humanActions').hasAttribute('hidden'));
- assert.isFalse(element.$$('.robotActions').hasAttribute('hidden'));
-
- // A robot comment with run ID should display plain text.
- element.set(['comment', 'robot_run_id'], 'text');
- element.editing = false;
- element.collapsed = false;
- flushAsynchronousOperations();
- assert.isNotOk(element.$$('.robotRun.link'));
- assert.notEqual(getComputedStyle(element.$$('.robotRun.text')).display,
- 'none');
-
- // A robot comment with run ID and url should display a link.
- element.set(['comment', 'url'], '/path/to/run');
- flushAsynchronousOperations();
- assert.notEqual(getComputedStyle(element.$$('.robotRun.link')).display,
- 'none');
- assert.equal(getComputedStyle(element.$$('.robotRun.text')).display,
- 'none');
- });
-
- test('collapsible drafts', () => {
- assert.isTrue(element.collapsed);
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isFalse(isVisible(element.$$('.actions')),
- 'actions are not visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
- assert.isTrue(isVisible(element.$$('.collapsedContent')),
- 'header middle content is visible');
-
- MockInteractions.tap(element.$.header);
- assert.isFalse(element.collapsed);
- assert.isTrue(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is visible');
- assert.isTrue(isVisible(element.$$('.actions')),
- 'actions are visible');
- assert.isNotOk(element.textarea, 'textarea is not visible');
- assert.isFalse(isVisible(element.$$('.collapsedContent')),
- 'header middle content is is not visible');
-
- // When the edit button is pressed, should still see the actions
- // and also textarea
- MockInteractions.tap(element.$$('.edit'));
- flushAsynchronousOperations();
- assert.isFalse(element.collapsed);
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isTrue(isVisible(element.$$('.actions')),
- 'actions are visible');
- assert.isTrue(isVisible(element.textarea), 'textarea is visible');
- assert.isFalse(isVisible(element.$$('.collapsedContent')),
- 'header middle content is not visible');
-
- // When toggle again, everything should be hidden except for textarea
- // and header middle content should be visible
- MockInteractions.tap(element.$.header);
- assert.isTrue(element.collapsed);
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isFalse(isVisible(element.$$('.actions')),
- 'actions are not visible');
- assert.isFalse(isVisible(element.$$('gr-textarea')),
- 'textarea is not visible');
- assert.isTrue(isVisible(element.$$('.collapsedContent')),
- 'header middle content is visible');
-
- // When toggle again, textarea should remain open in the state it was
- // before
- MockInteractions.tap(element.$.header);
- assert.isFalse(isVisible(element.$$('gr-formatted-text')),
- 'gr-formatted-text is not visible');
- assert.isTrue(isVisible(element.$$('.actions')),
- 'actions are visible');
- assert.isTrue(isVisible(element.textarea), 'textarea is visible');
- assert.isFalse(isVisible(element.$$('.collapsedContent')),
- 'header middle content is not visible');
- });
-
- test('draft creation/cancellation', done => {
- assert.isFalse(element.editing);
- MockInteractions.tap(element.$$('.edit'));
- assert.isTrue(element.editing);
-
- element._messageText = '';
- const eraseMessageDraftSpy = sandbox.spy(element, '_eraseDraftComment');
-
- // Save should be disabled on an empty message.
- let disabled = element.$$('.save').hasAttribute('disabled');
- assert.isTrue(disabled, 'save button should be disabled.');
- element._messageText = ' ';
- disabled = element.$$('.save').hasAttribute('disabled');
- assert.isTrue(disabled, 'save button should be disabled.');
-
- const updateStub = sinon.stub();
- element.addEventListener('comment-update', updateStub);
-
- let numDiscardEvents = 0;
- element.addEventListener('comment-discard', e => {
- numDiscardEvents++;
- assert.isFalse(eraseMessageDraftSpy.called);
- if (numDiscardEvents === 2) {
- assert.isFalse(updateStub.called);
- done();
- }
- });
- MockInteractions.tap(element.$$('.cancel'));
- element.flushDebouncer('fire-update');
- element._messageText = '';
- flushAsynchronousOperations();
- MockInteractions.pressAndReleaseKeyOn(element.textarea, 27); // esc
- });
-
- test('draft discard removes message from storage', done => {
- element._messageText = '';
- const eraseMessageDraftSpy = sandbox.spy(element, '_eraseDraftComment');
- sandbox.stub(element, '_closeConfirmDiscardOverlay');
-
- element.addEventListener('comment-discard', e => {
- assert.isTrue(eraseMessageDraftSpy.called);
- done();
- });
- element._handleConfirmDiscard({preventDefault: sinon.stub()});
- });
-
- test('storage is cleared only after save success', () => {
- element._messageText = 'test';
- const eraseStub = sandbox.stub(element, '_eraseDraftComment');
- sandbox.stub(element.$.restAPI, 'getResponseObject')
- .returns(Promise.resolve({}));
-
- sandbox.stub(element, '_saveDraft').returns(Promise.resolve({ok: false}));
-
- const savePromise = element.save();
- assert.isFalse(eraseStub.called);
- return savePromise.then(() => {
- assert.isFalse(eraseStub.called);
-
- element._saveDraft.restore();
- sandbox.stub(element, '_saveDraft')
- .returns(Promise.resolve({ok: true}));
- return element.save().then(() => {
- assert.isTrue(eraseStub.called);
- });
- });
- });
-
- test('_computeSaveDisabled', () => {
- const comment = {unresolved: true};
- const msgComment = {message: 'test', unresolved: true};
- assert.equal(element._computeSaveDisabled('', comment, false), true);
- assert.equal(element._computeSaveDisabled('test', comment, false), false);
- assert.equal(element._computeSaveDisabled('', msgComment, false), true);
- assert.equal(
- element._computeSaveDisabled('test', msgComment, false), false);
- assert.equal(
- element._computeSaveDisabled('test2', msgComment, false), false);
- assert.equal(element._computeSaveDisabled('test', comment, true), false);
- assert.equal(element._computeSaveDisabled('', comment, true), true);
- assert.equal(element._computeSaveDisabled('', comment, false), true);
- });
-
- suite('confirm discard', () => {
- let discardStub;
- let overlayStub;
- let mockEvent;
-
- setup(() => {
- discardStub = sandbox.stub(element, '_discardDraft');
- overlayStub = sandbox.stub(element, '_openOverlay')
- .returns(Promise.resolve());
- mockEvent = {preventDefault: sinon.stub()};
- });
-
- test('confirms discard of comments with message text', () => {
- element._messageText = 'test';
- element._handleDiscard(mockEvent);
- assert.isTrue(overlayStub.calledWith(element.confirmDiscardOverlay));
- assert.isFalse(discardStub.called);
- });
-
- test('no confirmation for comments without message text', () => {
- element._messageText = '';
- element._handleDiscard(mockEvent);
- assert.isFalse(overlayStub.called);
- assert.isTrue(discardStub.calledOnce);
- });
- });
-
- test('ctrl+s saves comment', done => {
- const stub = sinon.stub(element, 'save', () => {
- assert.isTrue(stub.called);
- stub.restore();
- done();
- return Promise.resolve();
- });
- element._messageText = 'is that the horse from horsing around??';
- element.editing = true;
- flushAsynchronousOperations();
- MockInteractions.pressAndReleaseKeyOn(
- element.textarea.$.textarea.textarea,
- 83, 'ctrl'); // 'ctrl + s'
- });
-
- test('draft saving/editing', done => {
- const fireStub = sinon.stub(element, 'fire');
- const cancelDebounce = sandbox.stub(element, 'cancelDebouncer');
-
- element.draft = true;
- MockInteractions.tap(element.$$('.edit'));
- element._messageText = 'good news, everyone!';
- element.flushDebouncer('fire-update');
- element.flushDebouncer('store');
- assert(fireStub.calledWith('comment-update'),
- 'comment-update should be sent');
- assert.isTrue(fireStub.calledOnce);
-
- element._messageText = 'good news, everyone!';
- element.flushDebouncer('fire-update');
- element.flushDebouncer('store');
- assert.isTrue(fireStub.calledOnce,
- 'No events should fire for text editing');
-
- MockInteractions.tap(element.$$('.save'));
-
- assert.isTrue(element.disabled,
- 'Element should be disabled when creating draft.');
-
- element._xhrPromise.then(draft => {
- assert(fireStub.calledWith('comment-save'),
- 'comment-save should be sent');
- assert(cancelDebounce.calledWith('store'));
-
- assert.deepEqual(fireStub.lastCall.args[1], {
- comment: {
- __commentSide: 'right',
- __draft: true,
- __draftID: 'temp_draft_id',
- id: 'baf0414d_40572e03',
- line: 5,
- message: 'saved!',
- path: '/path/to/file',
- updated: '2015-12-08 21:52:36.177000000',
- },
- patchNum: 1,
- });
- assert.isFalse(element.disabled,
- 'Element should be enabled when done creating draft.');
- assert.equal(draft.message, 'saved!');
- assert.isFalse(element.editing);
- }).then(() => {
- MockInteractions.tap(element.$$('.edit'));
- element._messageText = 'You’ll be delivering a package to Chapek 9, ' +
- 'a world where humans are killed on sight.';
- MockInteractions.tap(element.$$('.save'));
- assert.isTrue(element.disabled,
- 'Element should be disabled when updating draft.');
-
- element._xhrPromise.then(draft => {
- assert.isFalse(element.disabled,
- 'Element should be enabled when done updating draft.');
- assert.equal(draft.message, 'saved!');
- assert.isFalse(element.editing);
- fireStub.restore();
- done();
- });
- });
- });
-
- test('draft prevent save when disabled', () => {
- const saveStub = sandbox.stub(element, 'save').returns(Promise.resolve());
- element.showActions = true;
- element.draft = true;
- MockInteractions.tap(element.$.header);
- MockInteractions.tap(element.$$('.edit'));
- element._messageText = 'good news, everyone!';
- element.flushDebouncer('fire-update');
- element.flushDebouncer('store');
-
- element.disabled = true;
- MockInteractions.tap(element.$$('.save'));
- assert.isFalse(saveStub.called);
-
- element.disabled = false;
- MockInteractions.tap(element.$$('.save'));
- assert.isTrue(saveStub.calledOnce);
- });
-
- test('proper event fires on resolve, comment is not saved', done => {
- const save = sandbox.stub(element, 'save');
- element.addEventListener('comment-update', e => {
- assert.isTrue(e.detail.comment.unresolved);
- assert.isFalse(save.called);
- done();
- });
- MockInteractions.tap(element.$$('.resolve input'));
- });
-
- test('resolved comment state indicated by checkbox', () => {
- sandbox.stub(element, 'save');
- element.comment = {unresolved: false};
- assert.isTrue(element.$$('.resolve input').checked);
- element.comment = {unresolved: true};
- assert.isFalse(element.$$('.resolve input').checked);
- });
-
- test('resolved checkbox saves with tap when !editing', () => {
- element.editing = false;
- const save = sandbox.stub(element, 'save');
-
- element.comment = {unresolved: false};
- assert.isTrue(element.$$('.resolve input').checked);
- element.comment = {unresolved: true};
- assert.isFalse(element.$$('.resolve input').checked);
- assert.isFalse(save.called);
- MockInteractions.tap(element.$.resolvedCheckbox);
- assert.isTrue(element.$$('.resolve input').checked);
- assert.isTrue(save.called);
- });
-
- suite('draft saving messages', () => {
- test('_getSavingMessage', () => {
- assert.equal(element._getSavingMessage(0), 'All changes saved');
- assert.equal(element._getSavingMessage(1), 'Saving 1 draft...');
- assert.equal(element._getSavingMessage(2), 'Saving 2 drafts...');
- assert.equal(element._getSavingMessage(3), 'Saving 3 drafts...');
- });
-
- test('_show{Start,End}Request', () => {
- const updateStub = sandbox.stub(element, '_updateRequestToast');
- element._numPendingDraftRequests.number = 1;
-
- element._showStartRequest();
- assert.isTrue(updateStub.calledOnce);
- assert.equal(updateStub.lastCall.args[0], 2);
- assert.equal(element._numPendingDraftRequests.number, 2);
-
- element._showEndRequest();
- assert.isTrue(updateStub.calledTwice);
- assert.equal(updateStub.lastCall.args[0], 1);
- assert.equal(element._numPendingDraftRequests.number, 1);
-
- element._showEndRequest();
- assert.isTrue(updateStub.calledThrice);
- assert.equal(updateStub.lastCall.args[0], 0);
- assert.equal(element._numPendingDraftRequests.number, 0);
- });
- });
-
- test('cancelling an unsaved draft discards, persists in storage', () => {
- const discardSpy = sandbox.spy(element, '_fireDiscard');
- const storeStub = sandbox.stub(element.$.storage, 'setDraftComment');
- const eraseStub = sandbox.stub(element.$.storage, 'eraseDraftComment');
- element._messageText = 'test text';
- flushAsynchronousOperations();
- element.flushDebouncer('store');
-
- assert.isTrue(storeStub.called);
- assert.equal(storeStub.lastCall.args[1], 'test text');
- element._handleCancel({preventDefault: () => {}});
- assert.isTrue(discardSpy.called);
- assert.isFalse(eraseStub.called);
- });
-
- test('cancelling edit on a saved draft does not store', () => {
- element.comment.id = 'foo';
- const discardSpy = sandbox.spy(element, '_fireDiscard');
- const storeStub = sandbox.stub(element.$.storage, 'setDraftComment');
- element._messageText = 'test text';
- flushAsynchronousOperations();
- element.flushDebouncer('store');
-
- assert.isFalse(storeStub.called);
- element._handleCancel({preventDefault: () => {}});
- assert.isTrue(discardSpy.called);
- });
-
- test('deleting text from saved draft and saving deletes the draft', () => {
- element.comment = {id: 'foo', message: 'test'};
- element._messageText = '';
- const discardStub = sandbox.stub(element, '_discardDraft');
-
- element.save();
- assert.isTrue(discardStub.called);
- });
-
- test('_handleFix fires create-fix event', done => {
- element.addEventListener('create-fix-comment', e => {
- assert.deepEqual(e.detail, element._getEventPayload());
- done();
- });
- element.isRobotComment = true;
- flushAsynchronousOperations();
-
- MockInteractions.tap(element.$$('.fix'));
- });
- });
-</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html
index 3eb7b37..4c310b9 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.html
@@ -19,7 +19,7 @@
<link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
<link rel="import" href="../../core/gr-reporting/gr-reporting.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
-<link rel="import" href="../gr-diff-comment-thread/gr-diff-comment-thread.html">
+<link rel="import" href="../../shared/gr-comment-thread/gr-comment-thread.html">
<link rel="import" href="../gr-diff/gr-diff.html">
<dom-module id="gr-diff-host">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
index 0611a12..8b9d8066 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host.js
@@ -601,7 +601,7 @@
},
_createThreadElement(thread) {
- const threadEl = document.createElement('gr-diff-comment-thread');
+ const threadEl = document.createElement('gr-comment-thread');
threadEl.className = 'comment-thread';
threadEl.slot = `${thread.commentSide}-${thread.lineNum}`;
threadEl.comments = thread.comments;
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
index 53a60d7..ab9daec 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-host/gr-diff-host_test.html
@@ -256,7 +256,7 @@
threadEls[0].dispatchEvent(
new CustomEvent('thread-discard', {detail: {rootId: 4711}}));
const attachedThreads = element.queryAllEffectiveChildren(
- 'gr-diff-comment-thread');
+ 'gr-comment-thread');
assert.equal(attachedThreads.length, 1);
assert.equal(attachedThreads[0].rootId, 42);
});
@@ -1130,7 +1130,7 @@
commentSide, undefined, false));
let threads = Polymer.dom(element.$.diff)
- .queryDistributedElements('gr-diff-comment-thread');
+ .queryDistributedElements('gr-comment-thread');
assert.equal(threads.length, 1);
assert.equal(threads[0].commentSide, commentSide);
@@ -1151,7 +1151,7 @@
'3', 1, commentSide, range, true));
threads = Polymer.dom(element.$.diff)
- .queryDistributedElements('gr-diff-comment-thread');
+ .queryDistributedElements('gr-comment-thread');
assert.equal(threads.length, 2);
assert.equal(threads[1].commentSide, commentSide);
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
index 27e467d..6a9d88f 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection.js
@@ -83,7 +83,7 @@
targetClasses.push(SelectionClass.BLAME);
} else {
const commentSelected =
- this._elementDescendedFromClass(e.target, 'gr-diff-comment');
+ this._elementDescendedFromClass(e.target, 'gr-comment');
const side = this.diffBuilder.getSideByLineEl(lineEl);
targetClasses.push(side === 'left' ?
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index 5a56069..dadf8a7 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -221,7 +221,7 @@
[this.Shortcut.EXPAND_ALL_DIFF_CONTEXT]: '_handleExpandAllDiffContext',
[this.Shortcut.NEXT_UNREVIEWED_FILE]: '_handleNextUnreviewedFile',
- // Final two are actually handled by gr-diff-comment-thread.
+ // Final two are actually handled by gr-comment-thread.
[this.Shortcut.EXPAND_ALL_COMMENT_THREADS]: null,
[this.Shortcut.COLLAPSE_ALL_COMMENT_THREADS]: null,
};