Merge "Add 'created on' field to groups"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index c029031..aca1586 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -2102,6 +2102,20 @@
 +
 Path prefix for PolyGerrit's static resources if using a CDN.
 
+[[gerrit.ui]]gerrit.ui::
++
+Default UI when the user does not request a different preference via argument
+or cookie.
++
+* `GWT` for the old-style Google Web Toolkit-based interface.
+* `POLYGERRIT` for the new Polymer-based HTML5 Web interface.
++
+A sanity check during startup is performed that the value of
+gerrit.ui is an enabled UI.
++
+Defaults to GWT (if GWT is enabled) or POLYGERRIT (if POLYGERRIT is
+enabled and GWT is disabled)
+
 [[gitweb]]
 === Section gitweb
 
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index e1a25ca..e4b9a83 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -364,6 +364,8 @@
 parts in `project.config` file. That means from the UI a user can always
 assign permissions for that label on a branch, but this permission is then
 ignored if the label doesn't apply for that branch.
+Additionally, the `branch` modifier has no effect when the submit rule
+is customized in the rules.pl of the project or inherited from parent projects.
 
 [[label_example]]
 === Example
diff --git a/WORKSPACE b/WORKSPACE
index 8cb061e..4c89dc7 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1107,6 +1107,13 @@
     version = "1.0.0",
 )
 
+bower_archive(
+    name = "polymer-resin",
+    package = "polymer/polymer-resin",
+    sha1 = "4a60925f44d004b593e93aca828918b15b29526b",
+    version = "1.2.1-beta",
+)
+
 # bower test stuff
 
 bower_archive(
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/lfs/LfsDefinitions.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/lfs/LfsDefinitions.java
new file mode 100644
index 0000000..52aa9aa
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/api/lfs/LfsDefinitions.java
@@ -0,0 +1,36 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.api.lfs;
+
+import com.google.common.base.Joiner;
+
+public final class LfsDefinitions {
+  public static final String CONTENTTYPE_VND_GIT_LFS_JSON =
+      "application/vnd.git-lfs+json; charset=utf-8";
+
+  public static final String LFS_OBJECTS_PATH = "objects/batch";
+  public static final String LFS_LOCKS_PATH_REGEX = "locks(?:/(.*)(?:/unlock))?";
+  public static final String LFS_VERIFICATION_PATH = "locks/verify";
+  public static final String LFS_UNIFIED_PATHS_REGEX =
+      Joiner.on('|').join(LFS_OBJECTS_PATH, LFS_LOCKS_PATH_REGEX, LFS_VERIFICATION_PATH);
+  public static final String LFS_URL_WO_AUTH_REGEX_TEAMPLATE = "(?:/p/|/)(.+)(?:/info/lfs/)(?:%s)$";
+  public static final String LFS_URL_WO_AUTH_REGEX =
+      String.format(LFS_URL_WO_AUTH_REGEX_TEAMPLATE, LFS_UNIFIED_PATHS_REGEX);
+  public static final String LFS_URL_REGEX_TEMPLATE = "^(?:/a)?" + LFS_URL_WO_AUTH_REGEX_TEAMPLATE;
+  public static final String LFS_URL_REGEX =
+      String.format(LFS_URL_REGEX_TEMPLATE, LFS_UNIFIED_PATHS_REGEX);
+
+  private LfsDefinitions() {}
+}
diff --git a/gerrit-extension-api/src/test/java/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java b/gerrit-extension-api/src/test/java/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java
new file mode 100644
index 0000000..0be10ee
--- /dev/null
+++ b/gerrit-extension-api/src/test/java/com/google/gerrit/extensions/api/lfs/LfsDefinitionsTest.java
@@ -0,0 +1,82 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.api.lfs;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.junit.Test;
+
+public class LfsDefinitionsTest {
+  private static final String[] URL_PREFIXES = new String[] {"/", "/a/", "/p/", "/a/p/"};
+
+  @Test
+  public void noLfsEndPoint_noMatch() {
+    Pattern p = Pattern.compile(LfsDefinitions.LFS_URL_REGEX);
+    doesNotMatch(p, "/foo");
+    doesNotMatch(p, "/a/foo");
+    doesNotMatch(p, "/p/foo");
+    doesNotMatch(p, "/a/p/foo");
+
+    doesNotMatch(p, "/info/lfs/objects/batch");
+    doesNotMatch(p, "/info/lfs/objects/batch/foo");
+    doesNotMatch(p, "/info/lfs/locks");
+    doesNotMatch(p, "/info/lfs/locks/verify");
+    doesNotMatch(p, "/info/lfs/locks/unlock");
+    doesNotMatch(p, "/info/lfs/locks/lock_id/unlock");
+  }
+
+  @Test
+  public void matchingLfsEndpoint_projectNameCaptured() {
+    Pattern p = Pattern.compile(LfsDefinitions.LFS_URL_REGEX);
+    testProjectGetsMatched(p, "foo/bar/info/lfs/objects/batch", "foo/bar");
+    testProjectGetsMatched(p, "foo/bar/info/lfs/locks", "foo/bar");
+    testProjectGetsMatched(p, "foo/bar/info/lfs/locks/verify", "foo/bar");
+    testProjectAndLockIdGetMatched(
+        p, "foo/bar/info/lfs/locks/lock_id/unlock", "foo/bar", "lock_id");
+  }
+
+  private void testProjectAndLockIdGetMatched(
+      Pattern p, String url, String expectedProject, String expectedLockId) {
+    for (String prefix : URL_PREFIXES) {
+      matches(p, prefix + url, expectedProject, expectedLockId);
+    }
+  }
+
+  private void testProjectGetsMatched(Pattern p, String url, String expected) {
+    for (String prefix : URL_PREFIXES) {
+      matches(p, prefix + url, expected);
+    }
+  }
+
+  private void doesNotMatch(Pattern p, String input) {
+    Matcher m = p.matcher(input);
+    assertThat(m.matches()).isFalse();
+  }
+
+  private void matches(Pattern p, String input, String expectedProjectName) {
+    Matcher m = p.matcher(input);
+    assertThat(m.matches()).isTrue();
+    assertThat(m.group(1)).isEqualTo(expectedProjectName);
+  }
+
+  private void matches(Pattern p, String input, String expectedProjectName, String expectedLockId) {
+    Matcher m = p.matcher(input);
+    assertThat(m.matches()).isTrue();
+    assertThat(m.group(1)).isEqualTo(expectedProjectName);
+    assertThat(m.group(2)).isEqualTo(expectedLockId);
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
index 476404a..16dd167 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
@@ -111,6 +111,12 @@
       min = minList;
       max = maxList;
 
+      action.setAcceptableValues(
+          Arrays.asList(
+              PermissionRule.Action.ALLOW,
+              PermissionRule.Action.DENY,
+              PermissionRule.Action.BLOCK));
+
     } else {
       min = new RangeBox.Box();
       max = new RangeBox.Box();
@@ -144,15 +150,13 @@
     }
     force.setVisible(canForce);
     force.setEnabled(!readOnly);
+    action.getElement().setPropertyBoolean("disabled", readOnly);
 
     if (validRange != null) {
       min.setEnabled(!readOnly);
       max.setEnabled(!readOnly);
-      action.getElement().getStyle().setDisplay(Display.NONE);
-
     } else {
       rangeEditor.getStyle().setDisplay(Display.NONE);
-      action.getElement().setPropertyBoolean("disabled", readOnly);
     }
 
     if (readOnly) {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java
index d8ec107..3f3737d 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GitOverHttpModule.java
@@ -14,7 +14,7 @@
 
 package com.google.gerrit.httpd;
 
-import static com.google.gerrit.httpd.plugins.LfsPluginServlet.LFS_REST;
+import static com.google.gerrit.extensions.api.lfs.LfsDefinitions.LFS_URL_WO_AUTH_REGEX;
 
 import com.google.gerrit.extensions.client.GitBasicAuthPolicy;
 import com.google.gerrit.reviewdb.client.CoreDownloadSchemes;
@@ -26,7 +26,7 @@
 
 /** Configures Git access over HTTP with authentication. */
 public class GitOverHttpModule extends ServletModule {
-  private static final String LFS_URL_REGEX = "^(?:(?!/a/))" + LFS_REST;
+  private static final String NOT_AUTHORIZED_LFS_URL_REGEX = "^(?:(?!/a/))" + LFS_URL_WO_AUTH_REGEX;
 
   private final AuthConfig authConfig;
   private final DownloadConfig downloadConfig;
@@ -55,7 +55,7 @@
       serveRegex(git).with(GitOverHttpServlet.class);
     }
 
-    filterRegex(LFS_URL_REGEX).through(authFilter);
+    filterRegex(NOT_AUTHORIZED_LFS_URL_REGEX).through(authFilter);
     filter("/a/*").through(authFilter);
   }
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
index 1491345..937b24a 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginModule.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.httpd.plugins;
 
+import com.google.gerrit.extensions.api.lfs.LfsDefinitions;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.httpd.resources.Resource;
 import com.google.gerrit.httpd.resources.ResourceKey;
@@ -35,7 +36,7 @@
     serveRegex("^/(?:a/)?plugins/(.*)?$").with(HttpPluginServlet.class);
 
     bind(LfsPluginServlet.class);
-    serveRegex(LfsPluginServlet.URL_REGEX).with(LfsPluginServlet.class);
+    serveRegex(LfsDefinitions.LFS_URL_REGEX).with(LfsPluginServlet.class);
 
     bind(StartPluginListener.class)
         .annotatedWith(UniqueAnnotations.create())
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/LfsPluginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/LfsPluginServlet.java
index 0d3a7d5..7e013e6 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/LfsPluginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/LfsPluginServlet.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.httpd.plugins;
 
+import static com.google.gerrit.extensions.api.lfs.LfsDefinitions.CONTENTTYPE_VND_GIT_LFS_JSON;
 import static java.nio.charset.StandardCharsets.UTF_8;
 import static javax.servlet.http.HttpServletResponse.SC_NOT_IMPLEMENTED;
 
@@ -52,12 +53,6 @@
     implements StartPluginListener, ReloadPluginListener {
   private static final long serialVersionUID = 1L;
   private static final Logger log = LoggerFactory.getLogger(LfsPluginServlet.class);
-
-  public static final String LFS_REST = "(?:/p/|/)(.+)(?:/info/lfs/objects/batch)$";
-  public static final String URL_REGEX = "^(?:/a)?" + LFS_REST;
-
-  private static final String CONTENTTYPE_VND_GIT_LFS_JSON =
-      "application/vnd.git-lfs+json; charset=utf-8";
   private static final String MESSAGE_LFS_NOT_CONFIGURED =
       "{\"message\":\"No LFS plugin is configured to handle LFS requests.\"}";
 
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
index eed6ccc..a9cc460 100644
--- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
+++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/raw/PolyGerritIndexHtml.soy
@@ -40,6 +40,9 @@
   <link rel="stylesheet" href="{$staticResourcePath}/styles/fonts.css">{\n}
   <link rel="stylesheet" href="{$staticResourcePath}/styles/main.css">{\n}
   <script src="{$staticResourcePath}/bower_components/webcomponentsjs/webcomponents-lite.js"></script>{\n}
+  <link rel="import" href="{$staticResourcePath}/bower_components/polymer-resin/standalone/polymer-resin-debug.html">{\n}
+  <script>security.polymer_resin.install({lb}allowedIdentifierPrefixes: ['']{rb});</script>{\n}
+
   <link rel="preload" href="{$staticResourcePath}/elements/gr-app.js" as="script" crossorigin="anonymous">{\n}
   <link rel="import" href="{$staticResourcePath}/elements/gr-app.html">{\n}
 
diff --git a/gerrit-httpd/src/test/java/com/google/gerrit/httpd/plugins/LfsPluginServletTest.java b/gerrit-httpd/src/test/java/com/google/gerrit/httpd/plugins/LfsPluginServletTest.java
deleted file mode 100644
index b1f441d..0000000
--- a/gerrit-httpd/src/test/java/com/google/gerrit/httpd/plugins/LfsPluginServletTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-// 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.httpd.plugins;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.junit.Test;
-
-public class LfsPluginServletTest {
-
-  @Test
-  public void noLfsEndPoint_noMatch() {
-    Pattern p = Pattern.compile(LfsPluginServlet.URL_REGEX);
-    doesNotMatch(p, "/foo");
-    doesNotMatch(p, "/a/foo");
-    doesNotMatch(p, "/p/foo");
-    doesNotMatch(p, "/a/p/foo");
-
-    doesNotMatch(p, "/info/lfs/objects/batch");
-    doesNotMatch(p, "/info/lfs/objects/batch/foo");
-  }
-
-  @Test
-  public void matchingLfsEndpoint_projectNameCaptured() {
-    Pattern p = Pattern.compile(LfsPluginServlet.URL_REGEX);
-    matches(p, "/foo/bar/info/lfs/objects/batch", "foo/bar");
-    matches(p, "/a/foo/bar/info/lfs/objects/batch", "foo/bar");
-    matches(p, "/p/foo/bar/info/lfs/objects/batch", "foo/bar");
-    matches(p, "/a/p/foo/bar/info/lfs/objects/batch", "foo/bar");
-  }
-
-  private void doesNotMatch(Pattern p, String input) {
-    Matcher m = p.matcher(input);
-    assertThat(m.matches()).isFalse();
-  }
-
-  private void matches(Pattern p, String input, String expectedProjectName) {
-    Matcher m = p.matcher(input);
-    assertThat(m.matches()).isTrue();
-    assertThat(m.group(1)).isEqualTo(expectedProjectName);
-  }
-}
diff --git a/lib/js/bower_components.bzl b/lib/js/bower_components.bzl
index bb047bd70..9c3c63b 100644
--- a/lib/js/bower_components.bzl
+++ b/lib/js/bower_components.bzl
@@ -168,6 +168,15 @@
     seed = True,
   )
   bower_component(
+    name = "polymer-resin",
+    license = "//lib:LICENSE-polymer",
+    deps = [
+      ":polymer",
+      ":webcomponentsjs",
+    ],
+    seed = True,
+  )
+  bower_component(
     name = "polymer",
     license = "//lib:LICENSE-polymer",
     deps = [ ":webcomponentsjs" ],
diff --git a/polygerrit-ui/BUILD b/polygerrit-ui/BUILD
index 1f11cde..1b58a90 100644
--- a/polygerrit-ui/BUILD
+++ b/polygerrit-ui/BUILD
@@ -21,6 +21,7 @@
         "//lib/js:moment",
         "//lib/js:page",
         "//lib/js:polymer",
+        "//lib/js:polymer-resin",
         "//lib/js:promise-polyfill",
     ],
 )
diff --git a/polygerrit-ui/app/BUILD b/polygerrit-ui/app/BUILD
index 79ede8f..b292477 100644
--- a/polygerrit-ui/app/BUILD
+++ b/polygerrit-ui/app/BUILD
@@ -157,6 +157,7 @@
     size = "large",
     srcs = ["wct_test.sh"],
     data = [
+        "test/common-test-setup.html",
         "test/index.html",
         ":pg_code.zip",
         ":test_components.zip",
diff --git a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
index a42d804..99efede 100644
--- a/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/base-url-behavior/base-url-behavior_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
 <script src="../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../test/common-test-setup.html"/>
 <script>
   /** @type {String} */
   window.CANONICAL_PATH = '/r';
diff --git a/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html b/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
index 65908f2..19837cc 100644
--- a/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-change-table-behavior/gr-change-table-behavior_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
 <script src="../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../test/common-test-setup.html"/>
 <link rel="import" href="gr-change-table-behavior.html">
 
 <test-fixture id="basic">
diff --git a/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior_test.html b/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior_test.html
index 7ebb17a..0e10576 100644
--- a/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-patch-set-behavior/gr-patch-set-behavior_test.html
@@ -13,12 +13,12 @@
 See the License for the specific language governing permissions and
 limitations under the License.
 -->
+<title>gr-patch-set-behavior</title>
+
 <!-- Polymer included for the html import polyfill. -->
 <script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
 <script src="../../bower_components/web-component-tester/browser.js"></script>
-<title>gr-patch-set-behavior</title>
-
-<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../test/common-test-setup.html"/>
 <link rel="import" href="gr-patch-set-behavior.html">
 
 <script>
diff --git a/polygerrit-ui/app/behaviors/gr-path-list-behavior/gr-path-list-behavior_test.html b/polygerrit-ui/app/behaviors/gr-path-list-behavior/gr-path-list-behavior_test.html
index 848c744..37772d1 100644
--- a/polygerrit-ui/app/behaviors/gr-path-list-behavior/gr-path-list-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-path-list-behavior/gr-path-list-behavior_test.html
@@ -16,9 +16,9 @@
 <!-- Polymer included for the html import polyfill. -->
 <script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
 <script src="../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../test/common-test-setup.html"/>
 <title>gr-path-list-behavior</title>
 
-<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-path-list-behavior.html">
 
 <script>
diff --git a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
index 5fc9d54..f442c43 100644
--- a/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/gr-tooltip-behavior/gr-tooltip-behavior_test.html
@@ -19,8 +19,7 @@
 
 <script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
 <script src="../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../test/common-test-setup.html"/>
 <link rel="import" href="gr-tooltip-behavior.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
index da04c37..6906ea2 100644
--- a/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
 <script src="../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../test/common-test-setup.html"/>
 <link rel="import" href="keyboard-shortcut-behavior.html">
 
 <test-fixture id="basic">
diff --git a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
index 7a8376e..87fe67b 100644
--- a/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
+++ b/polygerrit-ui/app/behaviors/rest-client-behavior/rest-client-behavior_test.html
@@ -20,12 +20,12 @@
 
 <script src="../../bower_components/webcomponentsjs/webcomponents.min.js"></script>
 <script src="../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../test/common-test-setup.html"/>
 <script>
   /** @type {String} */
   window.CANONICAL_PATH = '/r';
 </script>
 
-<link rel="import" href="../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="../base-url-behavior/base-url-behavior.html">
 <link rel="import" href="rest-client-behavior.html">
 
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list_test.html b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list_test.html
index f9e08cd..b0a8bc9 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-admin-project-list/gr-admin-project-list_test.html
@@ -21,9 +21,7 @@
 <script src="../../../bower_components/page/page.js"></script>
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-admin-project-list.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-project/gr-admin-project.html b/polygerrit-ui/app/elements/admin/gr-admin-project/gr-admin-project.html
new file mode 100644
index 0000000..7c1b28d
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-admin-project/gr-admin-project.html
@@ -0,0 +1,201 @@
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../../bower_components/iron-autogrow-textarea/iron-autogrow-textarea.html">
+<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+
+<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
+<link rel="import" href="../../shared/gr-select/gr-select.html">
+<link rel="import" href="../../../styles/gr-form-styles.html">
+
+<dom-module id="gr-admin-project">
+  <template>
+    <style>
+      main {
+        margin: 2em 1em;
+      }
+
+      h1.edited:after {
+        color: #444;
+        content: ' *';
+      }
+    </style>
+    <style include="gr-form-styles"></style>
+      <main class="gr-form-styles read-only">
+        <h1 id="Title" class$="[[_computeHeaderClass(_configChanged)]]">
+          [[project]]</h1>
+        <div id="loading" hidden$="[[!_loading]]" hidden>Loading...</div>
+        <div id="form" hidden$="[[_loading]]" hidden>
+          <h2 id="Description">Description</h2>
+          <fieldset>
+            <iron-autogrow-textarea
+                id="descriptionInput"
+                class="description"
+                autocomplete="on"
+                placeholder="<Insert project description here>"
+                bind-value="{{_projectConfig.description}}"
+                disabled$="[[_readOnly]]"></iron-autogrow-textarea>
+          </fieldset>
+          <h2 id="Options">Project Options</h2>
+          <fieldset id="options">
+            <section>
+              <span class="title">State</span>
+              <span class="value">
+                <select
+                    id="stateSelect"
+                    is="gr-select"
+                    bind-value="{{_projectConfig.state}}"
+                    disabled$="[[_readOnly]]">
+                  <template is="dom-repeat" items=[[_states]]>
+                    <option value="[[item.value]]">[[item.label]]</option>
+                  </template>
+                </select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Submit type</span>
+              <span class="value">
+                 <select
+                    id="submitTypeSelect"
+                    is="gr-select"
+                    bind-value="{{_projectConfig.submit_type}}"
+                    disabled$="[[_readOnly]]">>
+                  <template is="dom-repeat" items="[[_submitTypes]]">
+                    <option value="[[item.value]]">[[item.label]]</option>
+                  </template>
+                </select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Allow content merges</span>
+              <span class="value">
+                 <select
+                    id="contentMergeSelect"
+                    is="gr-select"
+                    bind-value="{{_projectConfig.use_content_merge.configured_value}}"
+                    disabled$="[[_readOnly]]">>
+                  <template is="dom-repeat"
+                      items="[[_formatBooleanSelect(_projectConfig.use_content_merge)]]">
+                    <option value="[[item.value]]">[[item.label]]</option>
+                  </template>
+                 </select>
+              </span>
+            </section>
+            <section>
+              <span class="title">
+                Create a new change for every commit not in the target branch
+              </span>
+              <span class="value">
+                 <select
+                    id="newChangeSelect"
+                    is="gr-select"
+                    bind-value="{{_projectConfig.create_new_change_for_all_not_in_target.configured_value}}"
+                    disabled$="[[_readOnly]]">>
+                  <template is="dom-repeat"
+                      items="[[_formatBooleanSelect(_projectConfig.create_new_change_for_all_not_in_target)]]">
+                    <option value="[[item.value]]">[[item.label]]</option>
+                  </template>
+                 </select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Require Change-Id in commit message</span>
+              <span class="value">
+                 <select
+                    id="requireChangeIdSelect"
+                    is="gr-select"
+                    bind-value="{{_projectConfig.require_change_id.configured_value}}"
+                    disabled$="[[_readOnly]]">>
+                  <template is="dom-repeat"
+                      items="[[_formatBooleanSelect(_projectConfig.require_change_id)]]">
+                    <option value="[[item.value]]">[[item.label]]</option>
+                  </template>
+                 </select>
+              </span>
+            </section>
+            <section>
+              <span class="title">
+                Reject implicit merges when changes are pushed for review</span>
+              <span class="value">
+                 <select
+                    id="rejectImplicitMergesSelect"
+                    is="gr-select"
+                    bind-value="{{_projectConfig.reject_implicit_merges.configured_value}}"
+                    disabled$="[[_readOnly]]">>
+                  <template is="dom-repeat"
+                      items="[[_formatBooleanSelect(_projectConfig.reject_implicit_merges)]]">
+                    <option value="[[item.value]]">[[item.label]]</option>
+                  </template>
+                 </select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Maximum Git object size limit</span>
+              <span class="value">
+                <input
+                    id="maxGitObjSizeInput"
+                    bind-value="{{_projectConfig.max_object_size_limit.configured_value}}"
+                    is="iron-input"
+                    type="text"
+                    disabled$="[[_readOnly]]">
+              </span>
+            </section>
+          </fieldset>
+          <h2 id="Options">Contributor Agreements</h2>
+          <fieldset id="agreements">
+            <section>
+              <span class="title">
+                Require a valid contributor agreement to upload</span>
+              <span class="value">
+                <select
+                    id="contributorAgreementSelect"
+                    is="gr-select"
+                    bind-value="{{_projectConfig.use_contributor_agreements.configured_value}}"
+                    disabled$="[[_readOnly]]">>
+                <template is="dom-repeat"
+                    items="[[_formatBooleanSelect(_projectConfig.use_contributor_agreements)]]">
+                  <option value="[[item.value]]">[[item.label]]</option>
+                </template>
+                </select>
+              </span>
+            </section>
+            <section>
+              <span class="title">Require Signed-off-by in commit message</span>
+               <span class="value">
+                <select
+                    id="useSignedOffBySelect"
+                    is="gr-select"
+                    bind-value="{{_projectConfig.use_signed_off_by.configured_value}}"
+                    disabled$="[[_readOnly]]">>
+                <template is="dom-repeat"
+                    items="[[_formatBooleanSelect(_projectConfig.use_signed_off_by)]]">
+                  <option value="[[item.value]]">[[item.label]]</option>
+                </template>
+                </select>
+              </span>
+            </section>
+          </fieldset>
+          <!-- TODO @beckysiegel add plugin config widgets -->
+          <gr-button
+              on-tap="_handleSaveProjectConfig"
+              disabled$="[[_computeButtonDisabled(_readOnly, _configChanged)]]">Save changes</gr-button>
+        </div>
+      </main>
+    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </template>
+  <script src="gr-admin-project.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-project/gr-admin-project.js b/polygerrit-ui/app/elements/admin/gr-admin-project/gr-admin-project.js
new file mode 100644
index 0000000..3557aa9
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-admin-project/gr-admin-project.js
@@ -0,0 +1,177 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+(function() {
+  'use strict';
+
+  const STATES = {
+    active: {value: 'ACTIVE', label: 'Active'},
+    readOnly: {value: 'READ_ONLY', label: 'Read Only'},
+    hidden: {value: 'HIDDEN', label: 'Hidden'},
+  };
+
+  const SUBMIT_TYPES = {
+    mergeIfNecessary: {
+      value: 'MERGE_IF_NECESSARY',
+      label: 'Merge if necessary',
+    },
+    fastForwardOnly: {
+      value: 'FAST_FORWARD_ONLY',
+      label: 'Fast forward only',
+    },
+    rebaseIfNecessary: {
+      value: 'REBASE_IF_NECESSARY',
+      label: 'Rebase if necessary',
+    },
+    mergeAlways: {
+      value: 'MERGE_ALWAYS',
+      label: 'Merge always',
+    },
+    cherryPick: {
+      value: 'CHERRY_PICK',
+      label: 'Cherry pick',
+    },
+  };
+
+  Polymer({
+    is: 'gr-admin-project',
+
+    properties: {
+      params: Object,
+
+      _configChanged: {
+        type: Boolean,
+        value: false,
+      },
+      _loading: {
+        type: Boolean,
+        value: true,
+      },
+      _projectConfig: Object,
+      _readOnly: {
+        type: Boolean,
+        value: true,
+      },
+      _states: {
+        type: Array,
+        value() {
+          return Object.values(STATES);
+        },
+      },
+      _submitTypes: {
+        type: Array,
+        value() {
+          return Object.values(SUBMIT_TYPES);
+        },
+      },
+    },
+
+    observers: [
+      '_handleConfigChanged(_projectConfig.*)',
+    ],
+
+
+    attached() {
+      this._loadProject();
+    },
+
+    _loadProject() {
+      if (!this.project) { return Promise.resolve(); }
+
+      const promises = [];
+      promises.push(this._getLoggedIn().then(loggedIn => {
+        this._loggedIn = loggedIn;
+        if (loggedIn) {
+          this.$.restAPI.getProjectAccess(this.project).then(access => {
+            // If the user is not an owner, is_owner is not a property.
+            this._readOnly = !access[this.project].is_owner;
+          });
+        }
+      }));
+
+      promises.push(this.$.restAPI.getProjectConfig(this.project).then(
+          config => {
+            this._projectConfig = config;
+            if (!this._projectConfig.state) {
+              this._projectConfig.state = STATES.active.value;
+            }
+            this._loading = false;
+          }));
+
+      return Promise.all(promises);
+    },
+
+    _formatBooleanSelect(item) {
+      if (!item) { return; }
+      let inheritLabel = 'Inherit';
+      if (item.inherited_value) {
+        inheritLabel = `Inherit (${item.inherited_value})`;
+      }
+      return [
+        {
+          label: inheritLabel,
+          value: 'INHERIT',
+        },
+        {
+          label: 'True',
+          value: 'TRUE',
+        }, {
+          label: 'False',
+          value: 'FALSE',
+        },
+      ];
+    },
+
+    _isLoading() {
+      return this._loading || this._loading === undefined;
+    },
+
+    _getLoggedIn() {
+      return this.$.restAPI.getLoggedIn();
+    },
+
+    _formatProjectConfigForSave(p) {
+      const configInputObj = {};
+      for (const key in p) {
+        if (p.hasOwnProperty(key)) {
+          if (typeof p[key] === 'object') {
+            configInputObj[key] = p[key].configured_value;
+          } else {
+            configInputObj[key] = p[key];
+          }
+        }
+      }
+      return configInputObj;
+    },
+
+    _handleSaveProjectConfig() {
+      return this.$.restAPI.saveProjectConfig(this.project,
+          this._formatProjectConfigForSave(this._projectConfig)).then(() => {
+            this._configChanged = false;
+          });
+    },
+
+    _handleConfigChanged() {
+      if (this._isLoading()) { return; }
+      this._configChanged = true;
+    },
+
+    _computeButtonDisabled(readOnly, configChanged) {
+      return readOnly || !configChanged;
+    },
+
+    _computeHeaderClass(configChanged) {
+      return configChanged ? 'edited' : '';
+    },
+  });
+})();
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-project/gr-admin-project_test.html b/polygerrit-ui/app/elements/admin/gr-admin-project/gr-admin-project_test.html
new file mode 100644
index 0000000..beb1e34
--- /dev/null
+++ b/polygerrit-ui/app/elements/admin/gr-admin-project/gr-admin-project_test.html
@@ -0,0 +1,259 @@
+<!DOCTYPE html>
+<!--
+Copyright (C) 2017 The Android Open Source Project
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-admin-project</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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+
+<link rel="import" href="gr-admin-project.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-admin-project></gr-admin-project>
+  </template>
+</test-fixture>
+
+<script>
+  suite('gr-admin-project tests', () => {
+    let element;
+    let sandbox;
+    const PROJECT = 'test-project';
+
+    function getFormFields() {
+      const selects = Polymer.dom(element.root).querySelectorAll('select');
+      const textareas =
+          Polymer.dom(element.root).querySelectorAll('iron-autogrow-textarea');
+      const inputs = Polymer.dom(element.root).querySelectorAll('input');
+      return inputs.concat(textareas).concat(selects);
+    }
+
+    setup(() => {
+      sandbox = sinon.sandbox.create();
+      stub('gr-rest-api-interface', {
+        getLoggedIn() { return Promise.resolve(false); },
+        getProjectConfig() {
+          return Promise.resolve({
+            description: 'Access inherited by all other projects.',
+            use_contributor_agreements: {
+              value: false,
+              configured_value: 'FALSE',
+            },
+            use_content_merge: {
+              value: false,
+              configured_value: 'FALSE',
+            },
+            use_signed_off_by: {
+              value: false,
+              configured_value: 'FALSE',
+            },
+            create_new_change_for_all_not_in_target: {
+              value: false,
+              configured_value: 'FALSE',
+            },
+            require_change_id: {
+              value: false,
+              configured_value: 'FALSE',
+            },
+            reject_implicit_merges: {
+              value: false,
+              configured_value: 'FALSE',
+            },
+            max_object_size_limit: {},
+            submit_type: 'MERGE_IF_NECESSARY',
+          });
+        },
+      });
+      element = fixture('basic');
+    });
+
+    teardown(() => {
+      sandbox.restore();
+    });
+
+    test('loading displays before project config is loaded', () => {
+      assert.isFalse(element.$.loading.hidden);
+      assert.isTrue(element.$.form.hidden);
+    });
+
+    test('form defaults to read only', () => {
+      assert.isTrue(element._readOnly);
+    });
+
+    test('form defaults to read only when not logged in', done => {
+      element.project = PROJECT;
+      element._loadProject().then(() => {
+        assert.isTrue(element._readOnly);
+        done();
+      });
+    });
+
+    test('form defaults to read only when logged in and not admin', done => {
+      element.project = PROJECT;
+      sandbox.stub(element, '_getLoggedIn', () => {
+        return Promise.resolve(true);
+      });
+      sandbox.stub(element.$.restAPI, 'getProjectAccess', () => {
+        return Promise.resolve({'test-project': {}});
+      });
+      element._loadProject().then(() => {
+        assert.isTrue(element._readOnly);
+        done();
+      });
+    });
+
+    test('all form elements are disabled when not admin', done => {
+      element.project = PROJECT;
+      element._loadProject().then(() => {
+        flushAsynchronousOperations();
+        const formFields = getFormFields();
+        for (const field of formFields) {
+          assert.isTrue(field.hasAttribute('disabled'));
+        }
+        done();
+      });
+    });
+
+    test('_formatBooleanSelect', () => {
+      let item = {inherited_value: 'true'};
+      assert.deepEqual(element._formatBooleanSelect(item), [
+        {
+          label: 'Inherit (true)',
+          value: 'INHERIT',
+        },
+        {
+          label: 'True',
+          value: 'TRUE',
+        }, {
+          label: 'False',
+          value: 'FALSE',
+        },
+      ]);
+
+      // For items without inherited values
+      item = {};
+      assert.deepEqual(element._formatBooleanSelect(item), [
+        {
+          label: 'Inherit',
+          value: 'INHERIT',
+        },
+        {
+          label: 'True',
+          value: 'TRUE',
+        }, {
+          label: 'False',
+          value: 'FALSE',
+        },
+      ]);
+    });
+
+    suite('admin', () => {
+      setup(() => {
+        element.project = PROJECT;
+        sandbox.stub(element, '_getLoggedIn', () => {
+          return Promise.resolve(true);
+        });
+        sandbox.stub(element.$.restAPI, 'getProjectAccess', () => {
+          return Promise.resolve({'test-project': {is_owner: true}});
+        });
+      });
+
+      test('all form elements are enabled', done => {
+        element._loadProject().then(() => {
+          flushAsynchronousOperations();
+          const formFields = getFormFields();
+          for (const field of formFields) {
+            assert.isFalse(field.hasAttribute('disabled'));
+          }
+          assert.isFalse(element._loading);
+          done();
+        });
+      });
+
+      test('state gets set correctly', done => {
+        element._loadProject().then(() => {
+          assert.equal(element._projectConfig.state, 'ACTIVE');
+          done();
+        });
+      });
+
+      test('fields update and save correctly', done => {
+        const configInputObj = {
+          description: 'new description',
+          use_contributor_agreements: 'TRUE',
+          use_content_merge: 'TRUE',
+          use_signed_off_by: 'TRUE',
+          create_new_change_for_all_not_in_target: 'TRUE',
+          require_change_id: 'TRUE',
+          reject_implicit_merges: 'TRUE',
+          max_object_size_limit: 10,
+          submit_type: 'FAST_FORWARD_ONLY',
+          state: 'READ_ONLY',
+        };
+
+        const saveStub = sandbox.stub(element.$.restAPI, 'saveProjectConfig'
+            , () => {
+              return Promise.resolve({});
+            });
+
+        const button = Polymer.dom(element.root).querySelector('gr-button');
+
+        element._loadProject().then(() => {
+          assert.isTrue(button.hasAttribute('disabled'));
+          assert.isFalse(element.$.Title.classList.contains('edited'));
+          element.$.descriptionInput.bindValue = configInputObj.description;
+          element.$.stateSelect.bindValue = configInputObj.state;
+          element.$.submitTypeSelect.bindValue = configInputObj.submit_type;
+          element.$.contentMergeSelect.bindValue =
+              configInputObj.use_content_merge;
+          element.$.newChangeSelect.bindValue =
+              configInputObj.create_new_change_for_all_not_in_target;
+          element.$.requireChangeIdSelect.bindValue =
+              configInputObj.require_change_id;
+          element.$.rejectImplicitMergesSelect.bindValue =
+              configInputObj.reject_implicit_merges;
+          element.$.maxGitObjSizeInput.bindValue =
+              configInputObj.max_object_size_limit;
+          element.$.contributorAgreementSelect.bindValue =
+              configInputObj.use_contributor_agreements;
+          element.$.useSignedOffBySelect.bindValue =
+              configInputObj.use_signed_off_by;
+
+          assert.isFalse(button.hasAttribute('disabled'));
+          assert.isTrue(element.$.Title.classList.contains('edited'));
+
+          const formattedObj =
+              element._formatProjectConfigForSave(element._projectConfig);
+          assert.deepEqual(formattedObj, configInputObj);
+
+          element._handleSaveProjectConfig().then(() => {
+            assert.isTrue(button.hasAttribute('disabled'));
+            assert.isFalse(element.$.Title.classList.contains('edited'));
+            assert.isTrue(saveStub.lastCall.calledWithExactly(PROJECT,
+                configInputObj));
+            done();
+          });
+        });
+      });
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
index 6036293..cf55c5c 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item_test.html
@@ -20,9 +20,9 @@
 
 <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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-change-list-item.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
index a6d798d..e60907b 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-view/gr-change-list-view_test.html
@@ -21,8 +21,7 @@
 <script src="../../../bower_components/page/page.js"></script>
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-change-list-view.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
index 362f101..8277aa2 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.html
@@ -14,7 +14,9 @@
 limitations under the License.
 -->
 
+<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
 <link rel="import" href="../../../behaviors/gr-change-table-behavior/gr-change-table-behavior.html">
+<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
 <link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
@@ -39,6 +41,13 @@
         background-color: #eee;
         border-top: 1em solid #fff;
       }
+      .groupHeader a {
+        color: #000;
+        text-decoration: none;
+      }
+      .groupHeader a:hover {
+        text-decoration: underline;
+      }
       .headerRow + tr {
         border: none;
       }
@@ -61,17 +70,20 @@
           </th>
         </template>
       </tr>
-      <template is="dom-repeat" items="[[groups]]" as="changeGroup"
-          index-as="groupIndex">
-        <template is="dom-if" if="[[_groupTitle(groupIndex)]]">
+      <template is="dom-repeat" items="[[sections]]" as="changeSection"
+          index-as="sectionIndex">
+        <template is="dom-if" if="[[_sectionTitle(sectionIndex)]]">
           <tr class="groupHeader">
             <td class="cell"
                 colspan$="[[_computeColspan(changeTableColumns, labelNames)]]">
-              [[_groupTitle(groupIndex)]]
+              <a
+                  href$="[[_sectionHref(sectionIndex)]]">
+                [[_sectionTitle(sectionIndex)]]
+              </a>
             </td>
           </tr>
         </template>
-        <template is="dom-if" if="[[!changeGroup.length]]">
+        <template is="dom-if" if="[[!changeSection.length]]">
           <tr class="noChanges">
             <td class="cell"
                 colspan$="[[_computeColspan(changeTableColumns, labelNames)]]">
@@ -79,9 +91,9 @@
             </td>
           </tr>
         </template>
-        <template is="dom-repeat" items="[[changeGroup]]" as="change">
+        <template is="dom-repeat" items="[[changeSection]]" as="change">
           <gr-change-list-item
-              selected$="[[_computeItemSelected(index, groupIndex, selectedIndex)]]"
+              selected$="[[_computeItemSelected(index, sectionIndex, selectedIndex)]]"
               assigned$="[[_computeItemAssigned(account, change)]]"
               needs-review$="[[_computeItemNeedsReview(account, change, showReviewedState)]]"
               change="[[change]]"
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
index 5725c9c..e7b15af 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list.js
@@ -53,20 +53,20 @@
         observer: '_changesChanged',
       },
       /**
-       * ChangeInfo objects grouped into arrays. The groups and changes
+       * ChangeInfo objects grouped into arrays. The sections and changes
        * properties should not be used together.
        */
-      groups: {
+      sections: {
         type: Array,
         value() { return []; },
       },
-      groupTitles: {
+      sectionMetadata: {
         type: Array,
         value() { return []; },
       },
       labelNames: {
         type: Array,
-        computed: '_computeLabelNames(groups)',
+        computed: '_computeLabelNames(sections)',
       },
       selectedIndex: {
         type: Number,
@@ -89,9 +89,11 @@
     },
 
     behaviors: [
+      Gerrit.BaseUrlBehavior,
       Gerrit.ChangeTableBehavior,
       Gerrit.KeyboardShortcutBehavior,
       Gerrit.RESTClientBehavior,
+      Gerrit.URLEncodingBehavior,
     ],
 
     keyBindings: {
@@ -142,16 +144,16 @@
           NUMBER_FIXED_COLUMNS;
     },
 
-    _computeLabelNames(groups) {
-      if (!groups) { return []; }
+    _computeLabelNames(sections) {
+      if (!sections) { return []; }
       let labels = [];
       const nonExistingLabel = function(item) {
         return !labels.includes(item);
       };
-      for (let i = 0; i < groups.length; i++) {
-        const group = groups[i];
-        for (let j = 0; j < group.length; j++) {
-          const change = group[j];
+      for (let i = 0; i < sections.length; i++) {
+        const section = sections[i];
+        for (let j = 0; j < section.length; j++) {
+          const change = section[j];
           if (!change.labels) { continue; }
           const currentLabels = Object.keys(change.labels);
           labels = labels.concat(currentLabels.filter(nonExistingLabel));
@@ -165,18 +167,24 @@
     },
 
     _changesChanged(changes) {
-      this.groups = changes ? [changes] : [];
+      this.sections = changes ? [changes] : [];
     },
 
-    _groupTitle(groupIndex) {
-      if (groupIndex > this.groupTitles.length - 1) { return null; }
-      return this.groupTitles[groupIndex];
+    _sectionTitle(sectionIndex) {
+      if (sectionIndex > this.sectionMetadata.length - 1) { return null; }
+      return this.sectionMetadata[sectionIndex].name;
     },
 
-    _computeItemSelected(index, groupIndex, selectedIndex) {
+    _sectionHref(sectionIndex) {
+      if (sectionIndex > this.sectionMetadata.length - 1) { return null; }
+      const query = this.sectionMetadata[sectionIndex].query;
+      return `${this.getBaseUrl()}/q/${this.encodeURL(query, true)}`;
+    },
+
+    _computeItemSelected(index, sectionIndex, selectedIndex) {
       let idx = 0;
-      for (let i = 0; i < groupIndex; i++) {
-        idx += this.groups[i].length;
+      for (let i = 0; i < sectionIndex; i++) {
+        idx += this.sections[i].length;
       }
       idx += index;
       return idx == selectedIndex;
@@ -193,11 +201,11 @@
       return account._account_id === change.assignee._account_id;
     },
 
-    _getAggregateGroupsLen(groups) {
-      groups = groups || [];
+    _getAggregatesectionsLen(sections) {
+      sections = sections || [];
       let len = 0;
-      for (const group of this.groups) {
-        len += group.length;
+      for (const section of this.sections) {
+        len += section.length;
       }
       return len;
     },
@@ -207,7 +215,7 @@
           this.modifierPressed(e)) { return; }
 
       e.preventDefault();
-      const len = this._getAggregateGroupsLen(this.groups);
+      const len = this._getAggregatesectionsLen(this.sections);
       if (this.selectedIndex === len - 1) { return; }
       this.selectedIndex += 1;
     },
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
index 27f0603..aba32b03 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list/gr-change-list_test.html
@@ -20,9 +20,9 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <script src="../../../bower_components/page/page.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-change-list.html">
 
 <script>void(0);</script>
@@ -238,8 +238,8 @@
       assert.ok(noChangesMsg);
     });
 
-    test('empty groups', () => {
-      element.groups = [[], []];
+    test('empty sections', () => {
+      element.sections = [[], []];
       flushAsynchronousOperations();
       const listItems = Polymer.dom(element.root).querySelectorAll(
           'gr-change-list-item');
@@ -363,7 +363,7 @@
     });
   });
 
-  suite('gr-change-list groups', () => {
+  suite('gr-change-list sections', () => {
     let element;
 
     setup(() => {
@@ -372,7 +372,7 @@
 
     test('keyboard shortcuts', () => {
       element.selectedIndex = 0;
-      element.groups = [
+      element.sections = [
         [
           {_number: 0},
           {_number: 1},
@@ -389,7 +389,17 @@
           {_number: 8},
         ],
       ];
-      element.groupTitles = ['Group 1', 'Group 2', 'Group 3'];
+      element.sectionMetadata = [
+        {
+          name: 'Group 1',
+        },
+        {
+          name: 'Group 2',
+        },
+        {
+          name: 'Group 3',
+        },
+      ];
       flushAsynchronousOperations();
       const elementItems = Polymer.dom(element.root).querySelectorAll(
           'gr-change-list-item');
@@ -448,5 +458,17 @@
             items[i]._account_id === element.account._account_id);
       }
     });
+
+    test('_sectionHref', () => {
+      element.sectionMetadata = [
+        {query: 'is:open owner:self'},
+        {query: 'is:open ((reviewer:self -is:ignored) OR assignee:self)'},
+      ];
+
+      assert.equal(element._sectionHref(10), null);
+      assert.equal(element._sectionHref(0), '/q/is:open+owner:self');
+      assert.equal(element._sectionHref(1),
+          '/q/is:open+((reviewer:self+-is:ignored)+OR+assignee:self)');
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
index ce413ca..3042b46 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.html
@@ -14,6 +14,7 @@
 limitations under the License.
 -->
 
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 
@@ -44,8 +45,8 @@
           show-reviewed-state
           account="[[account]]"
           selected-index="{{viewState.selectedChangeIndex}}"
-          groups="{{_results}}"
-          group-titles="[[_groupTitles]]"></gr-change-list>
+          sections="{{_results}}"
+          section-metadata="[[sectionMetadata]]"></gr-change-list>
     </div>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
   </template>
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
index 58533b8..be955f6 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view.js
@@ -14,6 +14,23 @@
 (function() {
   'use strict';
 
+  const DEFAULT_SECTIONS = [
+    {
+      name: 'Outgoing reviews',
+      query: 'is:open owner:self',
+    },
+    {
+      name: 'Incoming reviews',
+      query: 'is:open ((reviewer:self -owner:self -is:ignored) OR ' +
+          'assignee:self)',
+    },
+    {
+      name: 'Recently closed',
+      query: 'is:closed (owner:self OR reviewer:self OR assignee:self) ' +
+          '-age:4w limit:10',
+    },
+  ];
+
   Polymer({
     is: 'gr-dashboard-view',
 
@@ -35,13 +52,9 @@
       },
 
       _results: Array,
-      _groupTitles: {
+      sectionMetadata: {
         type: Array,
-        value: [
-          'Outgoing reviews',
-          'Incoming reviews',
-          'Recently closed',
-        ],
+        value() { return DEFAULT_SECTIONS; },
       },
 
       /**
@@ -53,6 +66,18 @@
       },
     },
 
+    behaviors: [
+      Gerrit.RESTClientBehavior,
+    ],
+
+    get options() {
+      return this.listChangesOptionsToHex(
+          this.ListChangesOption.LABELS,
+          this.ListChangesOption.DETAILED_ACCOUNTS,
+          this.ListChangesOption.REVIEWED
+      );
+    },
+
     attached() {
       this.fire('title-change', {title: 'My Reviews'});
     },
@@ -62,7 +87,7 @@
      */
     _paramsChanged() {
       this._loading = true;
-      this._getDashboardChanges().then(results => {
+      this._getChanges().then(results => {
         this._results = results;
         this._loading = false;
       }).catch(err => {
@@ -71,8 +96,12 @@
       });
     },
 
-    _getDashboardChanges() {
-      return this.$.restAPI.getDashboardChanges();
+    _getChanges() {
+      return this.$.restAPI.getChanges(
+          null,
+          this.sectionMetadata.map(section => section.query),
+          null,
+          this.options);
     },
   });
 })();
diff --git a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
index fd635df..c67a50b 100644
--- a/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
+++ b/polygerrit-ui/app/elements/change-list/gr-dashboard-view/gr-dashboard-view_test.html
@@ -20,7 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-dashboard-view.html">
 
 <script>void(0);</script>
@@ -34,16 +34,20 @@
 <script>
   suite('gr-dashboard-view tests', () => {
     let element;
+    let sandbox;
 
     setup(() => {
       element = fixture('basic');
+      sandbox = sinon.sandbox.create();
+    });
+
+    teardown(() => {
+      sandbox.restore();
     });
 
     test('content is refreshed with same dropdown selected twice', () => {
-      const getChangesStub = sinon.stub(element, '_getDashboardChanges',
-          () => {
-            return Promise.resolve();
-          });
+      const getChangesStub = sandbox.stub(element, '_getChanges',
+          () => Promise.resolve());
 
       element.params = {view: 'gr-dashboard-view'};
 
diff --git a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html
index 54fde09..55213c1 100644
--- a/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html
+++ b/polygerrit-ui/app/elements/change/gr-account-entry/gr-account-entry_test.html
@@ -20,9 +20,9 @@
 
 <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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-account-entry.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list_test.html b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list_test.html
index 5520254..a3a3c64 100644
--- a/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-account-list/gr-account-list_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-account-list.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
index 8aa6bab..4e32116 100644
--- a/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-actions/gr-change-actions_test.html
@@ -20,9 +20,9 @@
 
 <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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-change-actions.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
index ae45da4..4b01a48 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="../../plugins/gr-plugin-host/gr-plugin-host.html">
 <link rel="import" href="gr-change-metadata.html">
 
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
index 5003a68..0d7b037 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-change-metadata.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 05c6c68..f9f873d 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -923,7 +923,10 @@
       return this.$.restAPI.getChangeDetail(this._changeNum,
           this._handleGetChangeDetailError.bind(this)).then(
           change => {
-                // Issue 4190: Coalesce missing topics to null.
+            if (!change) {
+              return '';
+            }
+            // Issue 4190: Coalesce missing topics to null.
             if (!change.topic) { change.topic = null; }
             if (!change.reviewer_updates) {
               change.reviewer_updates = null;
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index 5847e73..e91f372 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -20,9 +20,9 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <script src="../../../bower_components/page/page.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-change-view.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
index 5275f48..4c8b443 100644
--- a/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-comment-list/gr-comment-list_test.html
@@ -20,6 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-comment-list.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
index fc45f2a..bd6fdcb 100644
--- a/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
+++ b/polygerrit-ui/app/elements/change/gr-commit-info/gr-commit-info_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-commit-info.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html
index 2d6defd..107fb23 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-cherrypick-dialog/gr-confirm-cherrypick-dialog_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-confirm-cherrypick-dialog.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
index bb9651b..0ea7c49 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-rebase-dialog/gr-confirm-rebase-dialog_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-confirm-rebase-dialog.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
index 78b0dfc..09ba6bb 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-confirm-revert-dialog/gr-confirm-revert-dialog_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-confirm-revert-dialog.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
index 63d45c6..0bee5ba 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-download-dialog.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index 0bf61d2..055660d 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -323,6 +323,7 @@
             if="[[_isFileExpanded(file.__path, _expandedFilePaths.*)]]">
           <gr-diff
               no-auto-render
+              display-line="[[_displayLine]]"
               inline-index=[[index]]
               hidden="[[!_isFileExpanded(file.__path, _expandedFilePaths.*)]]"
               change-num="[[changeNum]]"
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 03c7a97..7621a9d 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -110,6 +110,7 @@
         type: Array,
         value() { return []; },
       },
+      _displayLine: Boolean,
     },
 
     behaviors: [
@@ -138,6 +139,7 @@
       'n': '_handleNKey',
       'p': '_handlePKey',
       'shift+a': '_handleCapitalAKey',
+      'esc': '_handleEscKey',
     },
 
     reload() {
@@ -482,6 +484,7 @@
       e.preventDefault();
       if (this._showInlineDiffs) {
         this.$.diffCursor.moveDown();
+        this._displayLine = true;
       } else {
         this.$.fileCursor.next();
         this.selectedIndex = this.$.fileCursor.index;
@@ -496,6 +499,7 @@
       e.preventDefault();
       if (this._showInlineDiffs) {
         this.$.diffCursor.moveUp();
+        this._displayLine = true;
       } else {
         this.$.fileCursor.previous();
         this.selectedIndex = this.$.fileCursor.index;
@@ -895,5 +899,12 @@
         }
       }
     },
+
+    _handleEscKey(e) {
+      if (this.shouldSuppressKeyboardShortcut(e) ||
+          this.modifierPressed(e)) { return; }
+      e.preventDefault();
+      this._displayLine = false;
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
index e022a74..f0f5403 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list_test.html
@@ -20,11 +20,11 @@
 
 <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="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-file-list.html">
 
 <script>void(0);</script>
@@ -1222,5 +1222,24 @@
       element._openSelectedFile();
       assert.isTrue(showStub.called);
     });
+
+    test('_displayLine', () => {
+      sandbox.stub(element, 'shouldSuppressKeyboardShortcut', () => false);
+      sandbox.stub(element, 'modifierPressed', () => false);
+      element._showInlineDiffs = true;
+      const mockEvent = {preventDefault() {}};
+
+      element._displayLine = false;
+      element._handleDownKey(mockEvent);
+      assert.isTrue(element._displayLine);
+
+      element._displayLine = false;
+      element._handleUpKey(mockEvent);
+      assert.isTrue(element._displayLine);
+
+      element._displayLine = true;
+      element._handleEscKey(mockEvent);
+      assert.isFalse(element._displayLine);
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html
index accdb35..15fe539 100644
--- a/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html
+++ b/polygerrit-ui/app/elements/change/gr-label-scores/gr-label-scores_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-label-scores.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
index 5f8b312..8638edd 100644
--- a/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
+++ b/polygerrit-ui/app/elements/change/gr-message/gr-message_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-message.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
index f6f2764..c47d63f 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-messages-list.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html
index 4a25405..6336433 100644
--- a/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-related-changes-list/gr-related-changes-list_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-related-changes-list.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
index b4a6171..12c08c6 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog-it_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-reply-dialog.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
index 1ee43fa..94587e2 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.html
@@ -248,13 +248,13 @@
       <section>
         <gr-button
             primary
-            disabled="[[!_isState(knownLatestState, 'latest')]]"
+            disabled="[[_isState(knownLatestState, 'not-latest')]]"
             class="action send"
             on-tap="_sendTapHandler">[[_sendButtonLabel]]</gr-button>
         </gr-button>
         <template is="dom-if" if="[[canBeStarted]]">
           <gr-button
-              disabled="[[!_isState(knownLatestState, 'latest')]]"
+              disabled="[[_isState(knownLatestState, 'not-latest')]]"
               class="action save"
               on-tap="_saveTapHandler">Save</gr-button>
         </template>
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
index 8218b43..7d7c3e2 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog.js
@@ -604,7 +604,7 @@
       if (!this.change) { return {}; }
       return {
         changeNum: this.change._number,
-        patchNum: this.patchNum,
+        patchNum: '@change',
         path: '@change',
       };
     },
diff --git a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
index ec2833a..9f5512b 100644
--- a/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reply-dialog/gr-reply-dialog_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-reply-dialog.html">
 
 <script>void(0);</script>
@@ -355,7 +354,7 @@
     test('_getStorageLocation', () => {
       const actual = element._getStorageLocation();
       assert.equal(actual.changeNum, changeNum);
-      assert.equal(actual.patchNum, patchNum);
+      assert.equal(actual.patchNum, '@change');
       assert.equal(actual.path, '@change');
     });
 
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
index 5ef99dd..c1640fa 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list.js
@@ -14,7 +14,7 @@
 (function() {
   'use strict';
 
-  const MAX_REVIEWERS_DISPLAYED = 10;
+  const MAX_REVIEWERS_DISPLAYED = 5;
 
   Polymer({
     is: 'gr-reviewer-list',
diff --git a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
index 632edd2..4e0541e6 100644
--- a/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-reviewer-list/gr-reviewer-list_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-reviewer-list.html">
 
 <script>void(0);</script>
@@ -189,9 +188,9 @@
           {value: {ccsOnly: true}}));
     });
 
-    test('no show all reviewers button with 10 reviewers', () => {
+    test('no show all reviewers button with 5 reviewers', () => {
       const reviewers = [];
-      for (let i = 0; i < 10; i++) {
+      for (let i = 0; i < 5; i++) {
         reviewers.push(
           {email: i+'reviewer@google.com', name: 'reviewer-' + i});
       }
@@ -207,8 +206,8 @@
       };
       flushAsynchronousOperations();
       assert.equal(element._hiddenReviewerCount, 0);
-      assert.equal(element._displayedReviewers.length, 10);
-      assert.equal(element._reviewers.length, 10);
+      assert.equal(element._displayedReviewers.length, 5);
+      assert.equal(element._reviewers.length, 5);
       assert.isTrue(element.$$('.hiddenReviewers').hidden);
     });
 
@@ -229,8 +228,8 @@
         },
       };
       flushAsynchronousOperations();
-      assert.equal(element._hiddenReviewerCount, 90);
-      assert.equal(element._displayedReviewers.length, 10);
+      assert.equal(element._hiddenReviewerCount, 95);
+      assert.equal(element._displayedReviewers.length, 5);
       assert.equal(element._reviewers.length, 100);
       assert.isFalse(element.$$('.hiddenReviewers').hidden);
 
diff --git a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
index 46f3ca3..6bda66f 100644
--- a/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
+++ b/polygerrit-ui/app/elements/core/gr-account-dropdown/gr-account-dropdown_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-account-dropdown.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
index ba10f09..5d1c461 100644
--- a/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
+++ b/polygerrit-ui/app/elements/core/gr-error-manager/gr-error-manager_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-error-manager.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
index f407ac5..71fc3e0 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-main-header.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
index bda03b1..08f72b7 100644
--- a/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
+++ b/polygerrit-ui/app/elements/core/gr-reporting/gr-reporting_test.html
@@ -20,7 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-reporting.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.js b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
index 6bb9005..d66b5f8 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.js
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.js
@@ -116,6 +116,14 @@
       };
     });
 
+    // Matches /admin/projects/<project>
+    page(/^\/admin\/projects\/(.+)$/, loadUser, data => {
+      app.params = {
+        view: 'gr-admin-project',
+        project: data.params[0],
+      };
+    });
+
     page('/admin/(.*)', loadUser, data => {
       restAPI.getLoggedIn().then(loggedIn => {
         if (loggedIn) {
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
index 54ca399..059ea7d 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
@@ -20,9 +20,9 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <script src="../../../bower_components/page/page.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-search-bar.html">
 <script src="../../../scripts/util.js"></script>
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
index 6c4a9d9..37b673b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-builder/gr-diff-builder_test.html
@@ -20,6 +20,7 @@
 
 <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>
 <script src="../gr-diff/gr-diff-line.js"></script>
 <script src="../gr-diff/gr-diff-group.js"></script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group_test.html b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group_test.html
index 056b74f..c2738460 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group_test.html
@@ -20,9 +20,9 @@
 
 <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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-diff-comment-thread-group.html">
 
 <script>void(0);</script>
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
index 85beca6..5a65f58 100644
--- 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
@@ -20,9 +20,9 @@
 
 <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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-diff-comment-thread.html">
 
 <script>void(0);</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
index e90dc41d..b46e38d 100644
--- 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
@@ -237,6 +237,7 @@
           id="editTextarea"
           class="editMessage"
           autocomplete="on"
+          monospace
           disabled="{{disabled}}"
           rows="4"
           text="{{_messageText}}"
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
index 4a1df77..c4d1273 100644
--- 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
@@ -20,10 +20,10 @@
 
 <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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-diff-comment.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
index 645093e..8be6e92 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor_test.html
@@ -20,9 +20,9 @@
 
 <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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="../gr-diff/gr-diff.html">
 <link rel="import" href="./gr-diff-cursor.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html
index b25b4fb..b237685 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-annotation_test.html
@@ -20,9 +20,9 @@
 
 <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="gr-annotation.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 
 <script>void(0);</script>
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
index c1254bd..f8e2a77 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-highlight/gr-diff-highlight_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-diff-highlight.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences_test.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences_test.html
index d201edf..f06cd3a 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-diff-preferences.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
index 4785351..beb8ed2 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-processor/gr-diff-processor_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-diff-processor.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html
index 39555b4..2a35a39 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-selection/gr-diff-selection_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-diff-selection.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
index 625af35..13d7140 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
@@ -20,10 +20,10 @@
 
 <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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-diff-view.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
index 90e5d06..32405cf 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff-group_test.html
@@ -19,6 +19,7 @@
 <title>gr-diff-group</title>
 
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <script src="gr-diff-line.js"></script>
 <script src="gr-diff-group.js"></script>
 
diff --git a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
index bd7adfe..d7c52fd 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff/gr-diff_test.html
@@ -20,9 +20,9 @@
 
 <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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
 <link rel="import" href="gr-diff.html">
 
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
index 0195eac..162936c 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select_test.html
@@ -20,9 +20,9 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <script src="../../../bower_components/page/page.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-patch-range-select.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html
index 1156ba0..afdf630 100644
--- a/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-ranged-comment-layer/gr-ranged-comment-layer_test.html
@@ -20,9 +20,9 @@
 
 <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="../gr-diff/gr-diff-line.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-ranged-comment-layer.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html
index 2beb6bc..cfa8c5f 100644
--- a/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-selection-action-box/gr-selection-action-box_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-selection-action-box.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
index 346a74a..98a6f8e 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-layer/gr-syntax-layer_test.html
@@ -20,7 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="../../shared/gr-rest-api-interface/mock-diff-response_test.html">
 <link rel="import" href="gr-syntax-layer.html">
 
diff --git a/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html b/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html
index 0a916d0..b46910a 100644
--- a/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-syntax-lib-loader/gr-syntax-lib-loader_test.html
@@ -20,7 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-syntax-lib-loader.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/gr-app-it_test.html b/polygerrit-ui/app/elements/gr-app-it_test.html
index 67c5139..7b134c1 100644
--- a/polygerrit-ui/app/elements/gr-app-it_test.html
+++ b/polygerrit-ui/app/elements/gr-app-it_test.html
@@ -20,8 +20,7 @@
 
 <script src="../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../test/common-test-setup.html"/>
 <link rel="import" href="gr-app.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index 33a059f..9d3e2f2 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -19,6 +19,7 @@
 <link rel="import" href="../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
 <link rel="import" href="../styles/app-theme.html">
 <link rel="import" href="./admin/gr-admin-project-list/gr-admin-project-list.html">
+<link rel="import" href="./admin/gr-admin-project/gr-admin-project.html">
 <link rel="import" href="./admin/gr-admin-view/gr-admin-view.html">
 <link rel="import" href="./change-list/gr-change-list-view/gr-change-list-view.html">
 <link rel="import" href="./change-list/gr-dashboard-view/gr-dashboard-view.html">
@@ -75,7 +76,7 @@
       }
       .errorView {
         align-items: center;
-        display: flex;
+        display: none;
         flex-direction: column;
         justify-content: center;
         position: absolute;
@@ -84,6 +85,9 @@
         bottom: 0;
         left: 0;
       }
+      .errorView.show {
+        display: flex;
+      }
       .errorEmoji {
         font-size: 2.6em;
       }
@@ -101,7 +105,7 @@
         color: #b71c1c;
       }
     </style>
-    <gr-fixed-panel>
+    <gr-fixed-panel id="header">
       <gr-main-header id="mainHeader" search-query="{{params.query}}">
       </gr-main-header>
     </gr-fixed-panel>
@@ -141,20 +145,22 @@
             params="[[params]]"
             id="projectList"></gr-admin-project-list>
       </template>
+      <template is="dom-if" if="[[_showAdminProject]]" restamp="true">
+        <gr-admin-project project="[[params.project]]"></gr-admin-project>
+      </template>
       <template is="dom-if" if="[[_showAdminView]]" restamp="true">
         <gr-admin-view path="[[_path]]"></gr-admin-view>
       </template>
       <template is="dom-if" if="[[_showCLAView]]" restamp="true">
         <gr-cla-view path="[[_path]]"></gr-cla-view>
       </template>
-      <div id="errorView" class="errorView" hidden>
+      <div id="errorView" class="errorView">
         <div class="errorEmoji">[[_lastError.emoji]]</div>
         <div class="errorText">[[_lastError.text]]</div>
         <div class="errorMoreInfo">[[_lastError.moreInfo]]</div>
       </div>
     </main>
-    <gr-fixed-panel
-        ready-for-measure="false">
+    <gr-fixed-panel id="footer">
       <footer r="contentinfo">
         <div>
           Powered by <a href="https://www.gerritcodereview.com/" rel="noopener"
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index 846c186..aa9b644 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -49,6 +49,7 @@
       _showDiffView: Boolean,
       _showSettingsView: Boolean,
       _showProjectListView: Boolean,
+      _showAdminProject: Boolean,
       _showAdminView: Boolean,
       _showCLAView: Boolean,
       _viewState: Object,
@@ -122,18 +123,21 @@
     },
 
     _viewChanged(view) {
-      this.$.errorView.hidden = true;
+      this.$.errorView.classList.remove('show');
       this.set('_showChangeListView', view === 'gr-change-list-view');
       this.set('_showDashboardView', view === 'gr-dashboard-view');
       this.set('_showChangeView', view === 'gr-change-view');
       this.set('_showDiffView', view === 'gr-diff-view');
       this.set('_showSettingsView', view === 'gr-settings-view');
       this.set('_showProjectListView', view === 'gr-admin-project-list');
+      this.set('_showAdminProject', view === 'gr-admin-project');
       this.set('_showAdminView', view === 'gr-admin-view');
       this.set('_showCLAView', view === 'gr-cla-view');
       if (this.params.justRegistered) {
         this.$.registration.open();
       }
+      this.$.header.unfloat();
+      this.$.footer.unfloat();
     },
 
     _loginTapHandler(e) {
@@ -163,7 +167,7 @@
         this.set(showProp, false);
       }
 
-      this.$.errorView.hidden = false;
+      this.$.errorView.classList.add('show');
       const response = e.detail.response;
       const err = {text: [response.status, response.statusText].join(' ')};
       if (response.status === 404) {
diff --git a/polygerrit-ui/app/elements/gr-app_test.html b/polygerrit-ui/app/elements/gr-app_test.html
index e25869b..905c5c4 100644
--- a/polygerrit-ui/app/elements/gr-app_test.html
+++ b/polygerrit-ui/app/elements/gr-app_test.html
@@ -20,7 +20,7 @@
 
 <script src="../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../test/common-test-setup.html"/>
 <link rel="import" href="gr-app.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.html b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.html
index f0e254f..07962d7 100644
--- a/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-external-style/gr-external-style_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-external-style.html">
 
 <test-fixture id="basic">
diff --git a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html
index 17f4875..1e81092 100644
--- a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html
+++ b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html
@@ -20,7 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-plugin-host.html">
 
 <test-fixture id="basic">
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
index d354f07..84fbc09 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-account-info.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html
index 15edb94..61093b9 100644
--- a/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-change-table-editor/gr-change-table-editor_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-change-table-editor.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html
index 608179e..fdbafdb 100644
--- a/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-email-editor/gr-email-editor_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-email-editor.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html
index c0cecdf..3c4eff2 100644
--- a/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-group-list/gr-group-list_test.html
@@ -20,7 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-group-list.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html
index acc9f60..bbe1555 100644
--- a/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-http-password/gr-http-password_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-http-password.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html
index f9e905d..7e12483 100644
--- a/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-menu-editor/gr-menu-editor_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-menu-editor.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
index 1f8a34a..b3ea3fa 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-registration-dialog.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
index 2d6c16d..517f208 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-settings-view.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html
index 09da19f..4273f7a 100644
--- a/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-ssh-editor/gr-ssh-editor_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-ssh-editor.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html
index 1e6ed7a..a93beb1 100644
--- a/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-watched-projects-editor.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html
index 2c43879..3b1e1de 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label_test.html
@@ -20,6 +20,7 @@
 
 <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-account-label.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html
index 29d1580..7e2e97b 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-account-link/gr-account-link_test.html
@@ -20,7 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-account-link.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html
index 6bbfcfb..b8dcb8d 100644
--- a/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-alert/gr-alert_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-alert.html">
 
 <script>
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
index c071f0c..db9440c 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html
@@ -20,9 +20,8 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-autocomplete-dropdown.html">
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 
 <script>void(0);</script>
 
diff --git a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
index 137e5f1..40add91 100644
--- a/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-autocomplete/gr-autocomplete_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-autocomplete.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
index 0a1ac67..4a51142 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
@@ -20,7 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-avatar.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html b/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html
index 536b777..de8b5b1 100644
--- a/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-button/gr-button_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-button.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html
index 6286efe..6c15a46 100644
--- a/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-change-star/gr-change-star_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-change-star.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog_test.html b/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog_test.html
index cb5d688..309e15c 100644
--- a/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-confirm-dialog/gr-confirm-dialog_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-confirm-dialog.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
index 7ab0088..a8102b5 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-cursor-manager.html">
 
 <script>void(0);</script>
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 8a3ea3a..2c15ef6 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
@@ -20,6 +20,7 @@
 
 <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-date-formatter.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
index 42ae98b..a034eb8 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown.js
@@ -98,6 +98,9 @@
     },
 
     _computeLinkURL(link) {
+      if (typeof link.url === 'undefined') {
+        return '';
+      }
       if (link.target) {
         return link.url;
       }
diff --git a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html
index 1815cca..c951cd4 100644
--- a/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-dropdown/gr-dropdown_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-dropdown.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html
index df98d5a..d8e5b21 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-content/gr-editable-content_test.html
@@ -20,6 +20,7 @@
 
 <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/iron-test-helpers/mock-interactions.js"></script>
 
 <link rel="import" href="gr-editable-content.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
index ffd1f81..9079f6f 100644
--- a/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-editable-label/gr-editable-label_test.html
@@ -20,6 +20,7 @@
 
 <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/iron-test-helpers/mock-interactions.js"></script>
 
 <link rel="import" href="gr-editable-label.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html
index 14f8499..8a1e110 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.html
@@ -21,12 +21,13 @@
     <style>
       :host {
         display: block;
-        height: var(--header-height);
+        min-height: var(--header-height);
         position: relative;
       }
       header {
         background: inherit;
         border: inherit;
+        display: inline;
         height: inherit;
       }
       .floating {
@@ -37,9 +38,7 @@
       }
     </style>
     <header id="header" class$="[[_computeHeaderClass(_headerFloating)]]">
-      <div class="contentWrapper">
-        <content></content>
-      </div>
+      <content></content>
     </header>
   </template>
   <script src="gr-fixed-panel.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
index 790324f..27cb373 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel.js
@@ -20,7 +20,7 @@
     properties: {
       readyForMeasure: {
         type: Boolean,
-        observer: '_maybeFloatHeader',
+        observer: '_readyForMeasureObserver',
       },
       keepOnScroll: {
         type: Boolean,
@@ -37,22 +37,34 @@
         type: Boolean,
         value: false,
       },
+      _observer: {
+        type: Object,
+        value: null,
+      },
+      _webComponentsReady: Boolean,
     },
 
     attached() {
       // Enable content measure unless blocked by param.
-      this.async(() => {
-        if (this.readyForMeasure !== false) {
-          this.readyForMeasure = true;
-        }
-      }, 1);
-      this.listen(window, 'scroll', '_handleScroll');
-      this.listen(window, 'resize', '_handleResize');
+      if (this.readyForMeasure !== false) {
+        this.readyForMeasure = true;
+      }
+      this.listen(window, 'resize', 'update');
+      this.listen(window, 'scroll', '_updateOnScroll');
+      this._observer = new MutationObserver(this.update.bind(this));
+      this._observer.observe(this.$.header, {childList: true, subtree: true});
     },
 
     detached() {
-      this.unlisten(window, 'scroll', '_handleScroll');
-      this.unlisten(window, 'resize', '_handleResize');
+      this.unlisten(window, 'scroll', '_updateOnScroll');
+      this.unlisten(window, 'resize', 'update');
+      this._observer.disconnect();
+    },
+
+    _readyForMeasureObserver(readyForMeasure) {
+      if (readyForMeasure) {
+        this.update();
+      }
     },
 
     _computeHeaderClass(headerFloating) {
@@ -63,22 +75,32 @@
       return window.scrollY;
     },
 
-    _handleResize() {
-      if (this._headerFloating) {
-        this._unfloatHeader();
-      }
-      this.debounce('resize', () => {
-        this._maybeFloatHeader();
-        this._handleScrollDebounced();
+    unfloat() {
+      this.$.header.style.top = '';
+      this._headerFloating = false;
+      this.customStyle['--header-height'] = '';
+      this.updateStyles();
+    },
+
+    update() {
+      this.debounce('update', () => {
+        this._updateDebounced();
       }, 100);
     },
 
-    _handleScroll() {
-      this._maybeFloatHeader();
-      this.debounce('scroll', this._handleScrollDebounced);
+    _updateOnScroll() {
+      this.debounce('update', () => {
+        this._updateDebounced();
+      });
     },
 
-    _handleScrollDebounced() {
+    _updateDebounced() {
+      this._isMeasured = false;
+      this._maybeFloatHeader();
+      this._reposition();
+    },
+
+    _reposition() {
       if (!this._headerFloating) {
         return;
       }
@@ -117,38 +139,33 @@
       if (rect.height === 0 && rect.width === 0) {
         return; // Not ready for measurement yet.
       }
-      const isVisible = (rect.top >= 0) && (rect.bottom <= window.innerHeight);
-      if (!isVisible) {
-        return;
-      }
       const top = document.body.scrollTop + rect.top;
-      this._topInitial = top;
       this._topLast = top;
+      this._headerHeight = rect.height;
+      this._topInitial =
+        this.getBoundingClientRect().top + document.body.scrollTop;
       this._isMeasured = true;
     },
 
+    _isFloatingNeeded() {
+      return this.keepOnScroll ||
+        document.body.scrollWidth > document.body.clientWidth;
+    },
+
     _maybeFloatHeader() {
-      if (!this.readyForMeasure) {
+      if (!this._isFloatingNeeded()) {
         return;
       }
       this._measure();
-      if (!this._headerFloating && this._isMeasured) {
+      if (this._isMeasured) {
         this._floatHeader();
       }
     },
 
-    _unfloatHeader() {
-      this.customStyle['--header-height'] = '';
-      this._headerFloating = false;
-      this.updateStyles();
-    },
-
     _floatHeader() {
-      const rect = this.$.header.getBoundingClientRect();
-      this._headerHeight = rect.height;
-      this.customStyle['--header-height'] = rect.height + 'px';
-      this._headerFloating = true;
+      this.customStyle['--header-height'] = this._headerHeight + 'px';
       this.updateStyles();
+      this._headerFloating = true;
     },
   });
 })();
diff --git a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
index 9f4291d..ee05f7d 100644
--- a/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-fixed-panel/gr-fixed-panel_test.html
@@ -41,6 +41,7 @@
     setup(() => {
       element = fixture('basic');
       sandbox = sinon.sandbox.create();
+      element.readyForMeasure = true;
     });
 
     teardown(() => {
@@ -51,20 +52,21 @@
       assert.equal(element.getBoundingClientRect().height, 100);
     });
 
-    test('scroll triggers _handleScroll', () => {
-      sandbox.stub(element, '_handleScroll');
+    test('scroll triggers _reposition', () => {
+      sandbox.stub(element, '_reposition');
       window.dispatchEvent(new CustomEvent('scroll'));
-      assert.isTrue(element._handleScroll.called);
+      element.flushDebouncer('update');
+      assert.isTrue(element._reposition.called);
     });
 
-    suite('_handleScroll', () => {
+    suite('_reposition', () => {
       const getHeaderTop = function() {
         return element.$.header.style.top;
       };
 
       const emulateScrollY = function(distance) {
         element._getScrollY.returns(distance);
-        element._handleScroll();
+        element._updateDebounced();
         element.flushDebouncer('scroll');
       };
 
@@ -73,20 +75,14 @@
         sandbox.stub(element, '_getScrollY').returns(0);
       });
 
-      test('is debounced', () => {
-        sandbox.stub(element, 'debounce');
-        element._handleScroll();
-        assert.isTrue(element.debounce.called);
-      });
-
       test('scrolls header along with document', () => {
         emulateScrollY(20);
-        assert.equal(getHeaderTop(), '-10px');
+        assert.equal(getHeaderTop(), '-12px');
       });
 
       test('does not stick to the top by default', () => {
         emulateScrollY(150);
-        assert.equal(getHeaderTop(), '-140px');
+        assert.equal(getHeaderTop(), '-100px');
       });
 
       test('sticks to the top if enabled', () => {
diff --git a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html
index 0192ea9..fe5b92f 100644
--- a/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-formatted-text/gr-formatted-text_test.html
@@ -20,7 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-formatted-text.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
index 24fee39..53d7345 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <!--
 This must refer to the element this interface is wrapping around. Otherwise
 breaking changes to gr-change-actions won’t be noticed.
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
index 73f3479..acb7905 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-reply-js-api_test.html
@@ -20,8 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <!--
      This must refer to the element this interface is wrapping around. Otherwise
      breaking changes to gr-reply-dialog won’t be noticed.
diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
index 40810b1..761fe2a 100644
--- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html
@@ -20,6 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-js-api-interface.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
index 3182653..d707d10 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-chip/gr-linked-chip_test.html
@@ -20,9 +20,9 @@
 
 <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/iron-test-helpers/mock-interactions.js"></script>
 
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-linked-chip.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
index 807278d..0042049 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
@@ -20,6 +20,7 @@
 
 <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-linked-text.html">
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
index 1e5fdaa..da85fe0 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.html
@@ -14,8 +14,8 @@
 limitations under the License.
 -->
 
-<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
 <link rel="import" href="../../../behaviors/gr-path-list-behavior/gr-path-list-behavior.html">
+<link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
 <script src="../../../bower_components/es6-promise/dist/es6-promise.min.js"></script>
 <script src="../../../bower_components/fetch/fetch.js"></script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index e371201..c404f25 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -26,66 +26,12 @@
     SEND_DIFF_DRAFT: 'sendDiffDraft',
   };
 
-  // Must be kept in sync with the ListChangesOption enum and protobuf.
-  const ListChangesOption = {
-    LABELS: 0,
-    DETAILED_LABELS: 8,
-
-    // Return information on the current patch set of the change.
-    CURRENT_REVISION: 1,
-    ALL_REVISIONS: 2,
-
-    // If revisions are included, parse the commit object.
-    CURRENT_COMMIT: 3,
-    ALL_COMMITS: 4,
-
-    // If a patch set is included, include the files of the patch set.
-    CURRENT_FILES: 5,
-    ALL_FILES: 6,
-
-    // If accounts are included, include detailed account info.
-    DETAILED_ACCOUNTS: 7,
-
-    // Include messages associated with the change.
-    MESSAGES: 9,
-
-    // Include allowed actions client could perform.
-    CURRENT_ACTIONS: 10,
-
-    // Set the reviewed boolean for the caller.
-    REVIEWED: 11,
-
-    // Include download commands for the caller.
-    DOWNLOAD_COMMANDS: 13,
-
-    // Include patch set weblinks.
-    WEB_LINKS: 14,
-
-    // Include consistency check results.
-    CHECK: 15,
-
-    // Include allowed change actions client could perform.
-    CHANGE_ACTIONS: 16,
-
-    // Include a copy of commit messages including review footers.
-    COMMIT_FOOTERS: 17,
-
-    // Include push certificate information along with any patch sets.
-    PUSH_CERTIFICATES: 18,
-
-    // Include change's reviewer updates.
-    REVIEWER_UPDATES: 19,
-
-    // Set the submittable boolean.
-    SUBMITTABLE: 20,
-  };
-
   Polymer({
     is: 'gr-rest-api-interface',
 
     behaviors: [
-      Gerrit.BaseUrlBehavior,
       Gerrit.PathListBehavior,
+      Gerrit.RESTClientBehavior,
     ],
 
     /**
@@ -192,6 +138,16 @@
           '/projects/' + encodeURIComponent(project) + '/config');
     },
 
+    getProjectAccess(project) {
+      return this._fetchSharedCacheURL(
+          '/access/?project=' + encodeURIComponent(project));
+    },
+
+    saveProjectConfig(project, config, opt_errFn, opt_ctx) {
+      return this.send('PUT', `/projects/${project}/config`, config, opt_errFn,
+          opt_ctx);
+    },
+
     getVersion() {
       return this._fetchSharedCacheURL('/config/server/version');
     },
@@ -422,57 +378,39 @@
       return window.innerWidth < MAX_UNIFIED_DEFAULT_WINDOW_WIDTH_PX;
     },
 
-    getChanges(changesPerPage, opt_query, opt_offset) {
-      const options = this._listChangesOptionsToHex(
-          ListChangesOption.LABELS,
-          ListChangesOption.DETAILED_ACCOUNTS
+    getChanges(opt_changesPerPage, opt_query, opt_offset, opt_options) {
+      const options = opt_options || this.listChangesOptionsToHex(
+          this.ListChangesOption.LABELS,
+          this.ListChangesOption.DETAILED_ACCOUNTS
       );
       // Issue 4524: respect legacy token with max sortkey.
       if (opt_offset === 'n,z') {
         opt_offset = 0;
       }
       const params = {
-        n: changesPerPage,
         O: options,
         S: opt_offset || 0,
       };
+      if (opt_changesPerPage) { params.n = opt_changesPerPage; }
       if (opt_query && opt_query.length > 0) {
         params.q = opt_query;
       }
       return this.fetchJSON('/changes/', null, null, params);
     },
 
-    getDashboardChanges() {
-      const options = this._listChangesOptionsToHex(
-          ListChangesOption.LABELS,
-          ListChangesOption.DETAILED_ACCOUNTS,
-          ListChangesOption.REVIEWED
-      );
-      const params = {
-        O: options,
-        q: [
-          'is:open owner:self',
-          'is:open ((reviewer:self -owner:self -is:ignored) OR assignee:self)',
-          'is:closed (owner:self OR reviewer:self OR assignee:self) -age:4w ' +
-            'limit:10',
-        ],
-      };
-      return this.fetchJSON('/changes/', null, null, params);
-    },
-
     getChangeActionURL(changeNum, opt_patchNum, endpoint) {
       return this._changeBaseURL(changeNum, opt_patchNum) + endpoint;
     },
 
     getChangeDetail(changeNum, opt_errFn, opt_cancelCondition) {
-      const options = this._listChangesOptionsToHex(
-          ListChangesOption.ALL_REVISIONS,
-          ListChangesOption.CHANGE_ACTIONS,
-          ListChangesOption.CURRENT_ACTIONS,
-          ListChangesOption.CURRENT_COMMIT,
-          ListChangesOption.DOWNLOAD_COMMANDS,
-          ListChangesOption.SUBMITTABLE,
-          ListChangesOption.WEB_LINKS
+      const options = this.listChangesOptionsToHex(
+          this.ListChangesOption.ALL_REVISIONS,
+          this.ListChangesOption.CHANGE_ACTIONS,
+          this.ListChangesOption.CURRENT_ACTIONS,
+          this.ListChangesOption.CURRENT_COMMIT,
+          this.ListChangesOption.DOWNLOAD_COMMANDS,
+          this.ListChangesOption.SUBMITTABLE,
+          this.ListChangesOption.WEB_LINKS
       );
       return this._getChangeDetail(
           changeNum, options, opt_errFn, opt_cancelCondition)
@@ -480,8 +418,8 @@
     },
 
     getDiffChangeDetail(changeNum, opt_errFn, opt_cancelCondition) {
-      const options = this._listChangesOptionsToHex(
-          ListChangesOption.ALL_REVISIONS
+      const options = this.listChangesOptionsToHex(
+          this.ListChangesOption.ALL_REVISIONS
       );
       return this._getChangeDetail(changeNum, options, opt_errFn,
           opt_cancelCondition);
@@ -620,9 +558,9 @@
     },
 
     getChangeConflicts(changeNum) {
-      const options = this._listChangesOptionsToHex(
-          ListChangesOption.CURRENT_REVISION,
-          ListChangesOption.CURRENT_COMMIT
+      const options = this.listChangesOptionsToHex(
+          this.ListChangesOption.CURRENT_REVISION,
+          this.ListChangesOption.CURRENT_COMMIT
       );
       const params = {
         O: options,
@@ -632,9 +570,9 @@
     },
 
     getChangeCherryPicks(project, changeID, changeNum) {
-      const options = this._listChangesOptionsToHex(
-          ListChangesOption.CURRENT_REVISION,
-          ListChangesOption.CURRENT_COMMIT
+      const options = this.listChangesOptionsToHex(
+          this.ListChangesOption.CURRENT_REVISION,
+          this.ListChangesOption.CURRENT_COMMIT
       );
       const query = [
         'project:' + project,
@@ -650,11 +588,11 @@
     },
 
     getChangesWithSameTopic(topic) {
-      const options = this._listChangesOptionsToHex(
-          ListChangesOption.LABELS,
-          ListChangesOption.CURRENT_REVISION,
-          ListChangesOption.CURRENT_COMMIT,
-          ListChangesOption.DETAILED_LABELS
+      const options = this.listChangesOptionsToHex(
+          this.ListChangesOption.LABELS,
+          this.ListChangesOption.CURRENT_REVISION,
+          this.ListChangesOption.CURRENT_COMMIT,
+          this.ListChangesOption.DETAILED_LABELS
       );
       const params = {
         O: options,
@@ -935,24 +873,6 @@
       });
     },
 
-    _changeBaseURL(changeNum, opt_patchNum) {
-      let v = '/changes/' + changeNum;
-      if (opt_patchNum) {
-        v += '/revisions/' + opt_patchNum;
-      }
-      return v;
-    },
-
-    // Derived from
-    // gerrit-extension-api/src/main/j/c/g/gerrit/extensions/client/ListChangesOption.java
-    _listChangesOptionsToHex(...args) {
-      let v = 0;
-      for (let i = 0; i < args.length; i++) {
-        v |= 1 << args[i];
-      }
-      return v.toString(16);
-    },
-
     _getCookie(name) {
       const key = name + '=';
       const cookies = document.cookie.split(';');
@@ -1038,6 +958,14 @@
       });
     },
 
+    _changeBaseURL(changeNum, opt_patchNum) {
+      let v = '/changes/' + changeNum;
+      if (opt_patchNum) {
+        v += '/revisions/' + opt_patchNum;
+      }
+      return v;
+    },
+
     setChangeTopic(changeNum, topic) {
       return this.send('PUT', '/changes/' + encodeURIComponent(changeNum) +
           '/topic', {topic});
@@ -1134,7 +1062,7 @@
     },
 
     deleteComment(changeNum, patchNum, commentID, reason) {
-      const url = this._changeBaseURL(changeNum, patchNum) +
+      const url = this.changeBaseURL(changeNum, patchNum) +
           '/comments/' + commentID + '/delete';
       return this.send('POST', url, {reason}).then(response =>
         this.getResponseObject(response));
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
index 9b8820a..8d5d4ef 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface_test.html
@@ -20,9 +20,9 @@
 
 <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="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 <link rel="import" href="gr-rest-api-interface.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html
index f27e068..ca1dc9f 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html
@@ -20,9 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <script src="../../../scripts/util.js"></script>
 <script src="gr-reviewer-updates-parser.js"></script>
 
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
index 0385f64..add07ea 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/mock-diff-response_test.html
@@ -15,7 +15,7 @@
 -->
 
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <dom-module id="mock-diff-response">
   <template></template>
   <script>
diff --git a/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html b/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html
index 83ae5af..fa8a54c 100644
--- a/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-select/gr-select_test.html
@@ -20,7 +20,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-select.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
index 4171939..ce8ec20 100644
--- a/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-storage/gr-storage_test.html
@@ -19,7 +19,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-storage.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
index 1c5dea3..637fbbfc 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
@@ -20,9 +20,8 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-textarea.html">
-<link rel="import" href="../../../bower_components/iron-test-helpers/iron-test-helpers.html">
 
 <script>void(0);</script>
 
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html
index 35c9a61..2fa02a3 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content_test.html
@@ -19,7 +19,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-tooltip-content.html">
 
 <script>void(0);</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html
index efc2a00..539176a 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.html
@@ -19,7 +19,7 @@
 
 <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
 <script src="../../../bower_components/web-component-tester/browser.js"></script>
-
+<link rel="import" href="../../../test/common-test-setup.html"/>
 <link rel="import" href="gr-tooltip.html">
 
 <script>void(0);</script>
@@ -42,6 +42,8 @@
       element.text = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit' +
           ', sed do eiusmod tempor incididunt ut labore et dolore magna aliqua';
       element.maxWidth = '50px';
+      flushAsynchronousOperations();
+
       assert.equal(getComputedStyle(element).width, '50px');
     });
   });
diff --git a/polygerrit-ui/app/index.html b/polygerrit-ui/app/index.html
index db9a1c5..df53a9e 100644
--- a/polygerrit-ui/app/index.html
+++ b/polygerrit-ui/app/index.html
@@ -29,6 +29,10 @@
 <link rel="stylesheet" href="/styles/fonts.css">
 <link rel="stylesheet" href="/styles/main.css">
 <script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
+<link rel="import" href="/bower_components/polymer-resin/standalone/polymer-resin-debug.html">
+<script>
+security.polymer_resin.install({allowedIdentifierPrefixes: ['']});
+</script>
 <link rel="preload" href="/elements/gr-app.js" as="script" crossorigin="anonymous">
 <link rel="import" href="/elements/gr-app.html">
 
diff --git a/polygerrit-ui/app/styles/gr-form-styles.html b/polygerrit-ui/app/styles/gr-form-styles.html
index bf5eb43..d8b799b 100644
--- a/polygerrit-ui/app/styles/gr-form-styles.html
+++ b/polygerrit-ui/app/styles/gr-form-styles.html
@@ -23,9 +23,6 @@
         border: none;
         margin: 0 0 2em 2em;
       }
-      .gr-form-styles.full-width fieldset {
-        margin: 0 0 2em 0;
-      }
       .gr-form-styles section {
         margin-bottom: .5em;
       }
@@ -40,12 +37,6 @@
         padding-right: .5em;
         width: 11em;
       }
-      .gr-form-styles.full-width .title {
-        color: #666;
-        font-weight: bold;
-        padding-right: .5em;
-        width: 22em;
-      }
       .gr-form-styles input {
         font-size: 1em;
       }
diff --git a/polygerrit-ui/app/test/common-test-setup.html b/polygerrit-ui/app/test/common-test-setup.html
new file mode 100644
index 0000000..9b6e7fa
--- /dev/null
+++ b/polygerrit-ui/app/test/common-test-setup.html
@@ -0,0 +1,7 @@
+<link rel="import"
+ href="../bower_components/polymer-resin/standalone/polymer-resin-debug.html" />
+<script>
+security.polymer_resin.install({allowedIdentifierPrefixes: ['']});
+</script>
+<link rel="import"
+ href="../bower_components/iron-test-helpers/iron-test-helpers.html" />
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 9a2e404..499bf53 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -30,6 +30,7 @@
     // This seemed to be flakey when it was farther down the list. Keep at the
     // beginning.
     'gr-app_test.html',
+    'admin/gr-admin-project/gr-admin-project_test.html',
     'admin/gr-admin-project-list/gr-admin-project-list_test.html',
     'change-list/gr-change-list-item/gr-change-list-item_test.html',
     'change-list/gr-change-list-view/gr-change-list-view_test.html',