Merge "DiffScreen: Make final fields package private and remove getters"
diff --git a/gerrit-server/BUCK b/gerrit-server/BUCK
index 58f2346..be38240 100644
--- a/gerrit-server/BUCK
+++ b/gerrit-server/BUCK
@@ -196,6 +196,7 @@
'//gerrit-server/src/main/prolog:common',
'//lib:args4j',
'//lib:grappa',
+ '//lib:gson',
'//lib:guava',
'//lib:guava-retrying',
'//lib:protobuf',
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/events/EventDeserializerTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/events/EventDeserializerTest.java
new file mode 100644
index 0000000..6a006cd
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/events/EventDeserializerTest.java
@@ -0,0 +1,70 @@
+// 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.
+
+package com.google.gerrit.server.events;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.gerrit.server.data.AccountAttribute;
+import com.google.gerrit.server.data.RefUpdateAttribute;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.junit.Test;
+
+public class EventDeserializerTest {
+
+ @Test
+ public void testRefUpdatedEvent() {
+ RefUpdatedEvent refUpdatedEvent = new RefUpdatedEvent();
+
+ RefUpdateAttribute refUpdatedAttribute = new RefUpdateAttribute();
+ refUpdatedAttribute.refName = "refs/heads/master";
+ refUpdatedEvent.refUpdate = createSupplier(refUpdatedAttribute);
+
+ AccountAttribute accountAttribute = new AccountAttribute();
+ accountAttribute.email = "some.user@domain.com";
+ refUpdatedEvent.submitter = createSupplier(accountAttribute);
+
+ Gson gsonSerializer = new GsonBuilder()
+ .registerTypeAdapter(Supplier.class, new SupplierSerializer()).create();
+ String serializedEvent = gsonSerializer.toJson(refUpdatedEvent);
+
+ Gson gsonDeserializer = new GsonBuilder()
+ .registerTypeAdapter(Event.class, new EventDeserializer())
+ .registerTypeAdapter(Supplier.class, new SupplierDeserializer())
+ .create();
+
+ RefUpdatedEvent e = (RefUpdatedEvent) gsonDeserializer
+ .fromJson(serializedEvent, Event.class);
+
+ assertThat(e).isNotNull();
+ assertThat(e.refUpdate).isInstanceOf(Supplier.class);
+ assertThat(e.refUpdate.get().refName)
+ .isEqualTo(refUpdatedAttribute.refName);
+ assertThat(e.submitter).isInstanceOf(Supplier.class);
+ assertThat(e.submitter.get().email).isEqualTo(accountAttribute.email);
+ }
+
+ private <T> Supplier<T> createSupplier(final T value) {
+ return Suppliers.memoize(new Supplier<T>() {
+ @Override
+ public T get() {
+ return value;
+ }
+ });
+ }
+}
diff --git a/lib/BUCK b/lib/BUCK
index 46ee264..1458c63 100644
--- a/lib/BUCK
+++ b/lib/BUCK
@@ -15,6 +15,7 @@
define_license(name = 'jgit')
define_license(name = 'jsch')
define_license(name = 'MPL1.1')
+define_license(name = 'moment')
define_license(name = 'ow2')
define_license(name = 'page.js')
define_license(name = 'polymer')
@@ -258,4 +259,3 @@
sha1 = 'd9a09f7732226af26bf99f19e2cffe0ae219db5b',
license = 'DO_NOT_DISTRIBUTE',
)
-
diff --git a/lib/LICENSE-moment b/lib/LICENSE-moment
new file mode 100644
index 0000000..9ee5374
--- /dev/null
+++ b/lib/LICENSE-moment
@@ -0,0 +1,22 @@
+Copyright (c) 2011-2016 Tim Wood, Iskren Chernev, Moment.js contributors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/lib/js/BUCK b/lib/js/BUCK
index 948f9b4..b0bdb67 100644
--- a/lib/js/BUCK
+++ b/lib/js/BUCK
@@ -257,6 +257,14 @@
)
bower_component(
+ name = 'moment',
+ package = 'moment/moment',
+ version = '2.12.0',
+ license = 'moment',
+ sha1 = '508d53de8f49ab87f03e209e5073e339107ed3e6',
+)
+
+bower_component(
name = 'neon-animation',
package = 'polymerelements/neon-animation',
version = '1.1.1',
diff --git a/polygerrit-ui/BUCK b/polygerrit-ui/BUCK
index 0a562f9..4d8144f 100644
--- a/polygerrit-ui/BUCK
+++ b/polygerrit-ui/BUCK
@@ -10,6 +10,7 @@
'//lib/js:iron-input',
'//lib/js:iron-overlay-behavior',
'//lib/js:iron-selector',
+ '//lib/js:moment',
'//lib/js:page',
'//lib/js:polymer',
'//lib/js:promise-polyfill',
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
index 814e206..3576b80 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown.html
@@ -83,7 +83,7 @@
</div>
</li>
<li><a href="/switch-account">Switch account</a></li>
- <li><a href="/logout">Logout</a></li>
+ <li><a href="/logout">Sign out</a></li>
</ul>
</div>
</iron-dropdown>
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
index af4a0e4..20405b9 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -148,7 +148,7 @@
<div class="rightItems">
<gr-search-bar value="{{searchQuery}}" role="search"></gr-search-bar>
<div class="accountContainer" id="accountContainer">
- <a class="loginButton" href="/login" on-tap="_loginTapHandler">Login</a>
+ <a class="loginButton" href="/login" on-tap="_loginTapHandler">Sign in</a>
<gr-account-dropdown account="[[_account]]"></gr-account-dropdown>
</div>
</div>
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
index 6d4b2ea..01deeda 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
@@ -15,6 +15,9 @@
-->
<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
+
+<script src="../../../bower_components/moment/moment.js"></script>
<dom-module id="gr-date-formatter">
<template>
@@ -23,7 +26,8 @@
display: inline;
}
</style>
- <span>[[_computeDateStr(dateStr)]]</span>
+ <span>[[_computeDateStr(dateStr, timeFormat)]]</span>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</template>
<script src="gr-date-formatter.js"></script>
</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
index cb77cc1..35347f5 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
@@ -19,10 +19,12 @@
DAY: 1000 * 60 * 60 * 24,
};
- var ShortMonthNames = [
- 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
- 'Nov', 'Dec'
- ];
+ var TimeFormats = {
+ TIME_12: 'h:mm A', // 2:14 PM
+ TIME_24: 'H:mm', // 14:14
+ MONTH_DAY: 'MMM DD', // Aug 29
+ MONTH_DAY_YEAR: 'MMM DD, YYYY', // Aug 29, 1997
+ };
Polymer({
is: 'gr-date-formatter',
@@ -31,46 +33,58 @@
dateStr: {
type: String,
value: null,
- notify: true
- }
+ notify: true,
+ },
+ timeFormat: {
+ type: String,
+ value: 'HHMM_24',
+ notify: true,
+ },
},
- _computeDateStr: function(dateStr) {
- return this._dateStr(this._parseDateStr(dateStr), new Date());
+ attached: function() {
+ this._fetchPreferences();
},
- _parseDateStr: function(dateStr) {
- if (!dateStr) { return null; }
- return util.parseDate(dateStr);
+ _fetchPreferences: function() {
+ this.$.restAPI.getPreferences().then(function(preferences) {
+ this.timeFormat = preferences && preferences.time_format;
+ }.bind(this));
},
- _dateStr: function(t, now) {
- if (!t) { return ''; }
- var diff = now.getTime() - t.getTime();
- if (diff < Duration.DAY && t.getDay() == now.getDay()) {
- // Within 24 hours and on the same day:
- // '2:14 AM'
- var pm = t.getHours() >= 12;
- var hours = t.getHours();
- if (hours == 0) {
- hours = 12;
- } else if (hours > 12) {
- hours = t.getHours() - 12;
+ /**
+ * Return true if date is within 24 hours and on the same day.
+ */
+ _isWithinDay: function(now, date) {
+ var diff = -date.diff(now);
+ return diff < Duration.DAY && date.day() == now.getDay();
+ },
+
+ /**
+ * Returns true if date is from one to six months.
+ */
+ _isWithinHalfYear: function(now, date) {
+ var diff = -date.diff(now);
+ return (date.day() != now.getDay() || diff >= Duration.DAY) &&
+ diff < 180 * Duration.DAY;
+ },
+
+ _computeDateStr: function(dateStr, timeFormat) {
+ if (!dateStr) { return ''; }
+ var date = moment(dateStr + 'Z');
+ if (!date.isValid()) { return ''; }
+ var now = new Date();
+ var format = TimeFormats.MONTH_DAY_YEAR;
+ if (this._isWithinDay(now, date)) {
+ if (this.timeFormat == 'HHMM_12') {
+ format = TimeFormats.TIME_12;
+ } else {
+ format = TimeFormats.TIME_24;
}
- var minutes = t.getMinutes() < 10 ? '0' + t.getMinutes() :
- t.getMinutes();
- return hours + ':' + minutes + (pm ? ' PM' : ' AM');
- } else if ((t.getDay() != now.getDay() || diff >= Duration.DAY) &&
- diff < 180 * Duration.DAY) {
- // From one to six months:
- // 'Aug 29'
- return ShortMonthNames[t.getMonth()] + ' ' + t.getDate();
- } else if (diff >= 180 * Duration.DAY) {
- // More than six months:
- // 'Aug 29, 1997'
- return ShortMonthNames[t.getMonth()] + ' ' + t.getDate() + ', ' +
- t.getFullYear();
+ } else if (this._isWithinHalfYear(now, date)) {
+ format = TimeFormats.MONTH_DAY;
}
+ return date.format(format);
},
});
})();
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
index 9bba517..932cba4 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
@@ -34,51 +34,107 @@
suite('gr-date-formatter tests', function() {
var element;
- setup(function() {
- element = fixture('basic');
- });
-
- test('date is parsed correctly', function() {
- assert.notOk((new Date('foo')).valueOf());
- var d = element._parseDateStr(element.getAttribute('date-str'));
- assert.isAbove(d.valueOf(), 0);
- });
-
+ /**
+ * Parse server-formatter date and normalize into current timezone.
+ */
function normalizedDate(dateStr) {
- var d = new Date(dateStr);
+ var d = new Date(util.parseDate(dateStr));
d.setMinutes(d.getMinutes() + d.getTimezoneOffset());
return d;
}
function testDates(nowStr, dateStr, expected) {
- var now = normalizedDate(nowStr);
- var t = normalizedDate(dateStr);
- assert.equal(element._dateStr(t, now), expected);
+ // Normalize and convert the date to mimic server response.
+ dateStr = normalizedDate(dateStr)
+ .toJSON().replace('T', ' ').slice(0, -1);
+ var clock = sinon.useFakeTimers(
+ new Date(normalizedDate(nowStr)).getTime());
+ element.dateStr = dateStr;
+ assert.equal(element.$$('span').textContent, expected);
+ clock.restore();
}
- test('dates strings are correct', function() {
- // Within 24 hours on same day.
- testDates('2015-07-29T20:34:00.000Z',
- '2015-07-29T15:34:00.000Z',
- '3:34 PM');
- testDates('2016-01-27T17:41:00.000Z',
- '2016-01-27T12:41:00.000Z',
- '12:41 PM');
+ function stubPreferences(preferences) {
+ var preferencesPromise = Promise.resolve(preferences);
+ stub('gr-rest-api-interface', {
+ getPreferences: sinon.stub().returns(preferencesPromise),
+ });
+ return preferencesPromise;
+ }
- // Within 24 hours on different days.
- testDates('2015-07-29T03:34:00.000Z',
- '2015-07-28T20:25:00.000Z',
- 'Jul 28');
+ suite('not signed in', function() {
+ setup(function() {
+ return stubPreferences(null).then(function() {
+ element = fixture('basic');
+ });
+ });
- // More than 24 hours. Less than six months.
- testDates('2015-07-29T20:34:00.000Z',
- '2015-06-15T03:25:00.000Z',
- 'Jun 15');
+ test('invalid dates are quietly rejected', function() {
+ assert.notOk((new Date('foo')).valueOf());
+ element.dateStr = 'foo';
+ assert.equal(element.$$('span').textContent, '');
+ });
- // More than six months.
- testDates('2015-09-15T20:34:00.000Z',
- '2015-01-15T03:25:00.000Z',
- 'Jan 15, 2015');
+ test('Within 24 hours on same day', function() {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '15:34');
+ testDates('2016-01-27 17:41:14.985000000',
+ '2016-01-27 12:41:14.985000000',
+ '12:41');
+ });
+
+ test('Within 24 hours on different days', function() {
+ testDates('2015-07-29 03:34:14.985000000',
+ '2015-07-28 20:25:14.985000000',
+ 'Jul 28');
+ });
+
+ test('More than 24 hours but less than six months', function() {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-06-15 03:25:14.985000000',
+ 'Jun 15');
+ });
+
+ test('More than six months', function() {
+ testDates('2015-09-15 20:34:00.000000000',
+ '2015-01-15 03:25:00.000000000',
+ 'Jan 15, 2015');
+ });
+ });
+
+ suite('signed in with 12 hours time format preference', function() {
+ setup(function() {
+ return stubPreferences({time_format: 'HHMM_12'}).then(function() {
+ element = fixture('basic');
+ });
+ });
+
+ test('Within 24 hours on same day', function() {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '3:34 PM');
+ testDates('2016-01-27 17:41:14.985000000',
+ '2016-01-27 12:41:14.985000000',
+ '12:41 PM');
+ });
+ });
+
+ suite('signed in with 24 hours time format preference', function() {
+ setup(function() {
+ return stubPreferences({time_format: 'HHMM_24'}).then(function() {
+ element = fixture('basic');
+ });
+ });
+
+ test('Within 24 hours on same day', function() {
+ testDates('2015-07-29 20:34:14.985000000',
+ '2015-07-29 15:34:14.985000000',
+ '15:34');
+ testDates('2016-01-27 17:41:14.985000000',
+ '2016-01-27 12:41:14.985000000',
+ '12:41');
+ });
});
});
</script>