Mail footer discoverability helper

Add info regarding mail footers and filter authoring to PolyGerrit and
to GWT UI settings screens and include a mention in the change footer
template.

Feature: Issue 4982
Change-Id: I3443af49fcdc16ca941ee7cf2b5e33c1106f3b1d
diff --git a/Documentation/user-notify.txt b/Documentation/user-notify.txt
index 4dc4880..d83ca87 100644
--- a/Documentation/user-notify.txt
+++ b/Documentation/user-notify.txt
@@ -142,6 +142,108 @@
 access is automatically checked by Gerrit and therefore does not
 need to use the `visibleto:` operator in the filter.
 
+[[footers]]
+== Email Footers
+
+Notification emails related to changes include metadata about the change
+to support writing mail filters. This metadata is included in the form
+of footers in the message content. For HTML emails, these footers are
+hidden, but they can be examined by viewing the HTML source of messages.
+
+In this way users may apply filters and rules to their incoming Gerrit
+notifications using the values of these footers. For example a Gmail
+filter to find emails regarding reviews that you are a reviewer of might
+take the following form.
+
+----
+  "Gerrit-Reviewer: Your Name <your.email@example.com>"
+----
+
+[[Gerrit-MessageType]]Gerrit-MessageType::
+
+The message type footer states the type of the message and will take one
+of the following values.
+
+* abandon
+* comment
+* deleteReviewer
+* deleteVote
+* merged
+* newchange
+* newpatchset
+* restore
+* revert
+* setassignee
+
+[[Gerrit-Change-Id]]Gerrit-Change-Id::
+
+The change ID footer states the ID of the change, such as
+`I3443af49fcdc16ca941ee7cf2b5e33c1106f3b1d`.
+
+[[Gerrit-Change-Number]]Gerrit-Change-Number::
+
+The change number footer states the numeric ID of the change, for
+example `92191`.
+
+[[Gerrit-PatchSet]]Gerrit-PatchSet::
+
+The patch set footer states the number of the patch set that the email
+relates to. For example, a notification email for a vote being set on
+the seventh patch set will take a value of `7`.
+
+[[Gerrit-Owner]]Gerrit-Owner::
+
+The owner footer states the name and email address of the change's
+owner. For example, `Owner Name <owner@example.com>`.
+
+[[Gerrit-Reviewer]]Gerrit-Reviewer::
+
+The reviewer footers list the names and email addresses of the change's
+reviewrs. One footer is included for each reviewer. For example, if a
+change has two reviewers, the footers might include:
+
+----
+  Gerrit-Reviewer: Reviewer One <one@example.com>
+  Gerrit-Reviewer: Reviewer Two <two@example.com>
+----
+
+[[Gerrit-CC]]Gerrit-CC::
+
+The CC footers list the names and email addresses of those who have been
+CC'd on the change. One footer is included for each reviewer. For
+example, if a change CCs two users, the footers might include:
+
+----
+  Gerrit-CC: User One <one@example.com>
+  Gerrit-CC: User Two <two@example.com>
+----
+
+[[Gerrit-Project]]Gerrit-Project::
+
+The project footer states the project to which the change belongs.
+
+[[Gerrit-Branch]]Gerrit-Branch::
+
+The branch footer states the abbreviated name of the branch that the
+change targets.
+
+[[Gerrit-Comment-Date]]Gerrit-Comment-Date::
+
+In comment emails, the comment date footer states the date that the
+comment was posted.
+
+[[Gerrit-HasComments]]Gerrit-HasComments::
+
+In comment emails, the has-comments footer states whether inline
+comments had been posted in that notification using "Yes" or "No", for
+example `Gerrit-HasComments: Yes`.
+
+[[Gerrit-HasLabels]]Gerrit-HasLabels::
+
+In comment emails, the has-labels footer states whether label votes had
+been posted in that notification using "Yes" or "No", for
+example `Gerrit-HasLabels: No`.
+
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
index 314871e..9799723 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.java
@@ -239,6 +239,10 @@
 
   String errorDialogTitleRegisterNewEmail();
 
+  String emailFilterHelpTitle();
+
+  String emailFilterHelp();
+
   String newAgreement();
 
   String agreementName();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
index d31abdb..a975b29 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/AccountConstants.properties
@@ -158,7 +158,74 @@
   <p>A confirmation link will be sent by email to this address.</p>\
   <p>You must click on the link to complete the registration and make the address available for selection.</p>
 errorDialogTitleRegisterNewEmail = Email Registration Failed
-
+emailFilterHelpTitle = Mail Filters
+emailFilterHelp = \
+  <p>\
+    Gerrit emails include metadata about the change to support \
+    writing mail filters.\
+  </p>\
+  <p>\
+    Here are some example Gmail queries that can be used for filters or \
+    for searching through archived messages. View the \
+    <a href="https://gerrit-review.googlesource.com/Documentation/user-notify.html"\
+        target="_blank" rel="nofollow">Gerrit documentation</a> for \
+    the complete set of footers.\
+  </p>\
+  <table>\
+    <tbody>\
+      <tr><th>Name</th><th>Query</th></tr>\
+      <tr>\
+        <td>Changes requesting my review</td>\
+        <td>\
+          <code>\
+            "Gerrit-Reviewer: <em>Your Name</em>\
+            &lt;<em>your.email@example.com</em>&gt;"\
+          </code>\
+        </td>\
+      </tr>\
+      <tr>\
+        <td>Changes from a specific owner</td>\
+        <td>\
+          <code>\
+            "Gerrit-Owner: <em>Owner name</em>\
+            &lt;<em>owner.email@example.com</em>&gt;"\
+          </code>\
+        </td>\
+      </tr>\
+      <tr>\
+        <td>Changes targeting a specific branch</td>\
+        <td>\
+          <code>\
+            "Gerrit-Branch: <em>branch-name</em>"\
+          </code>\
+        </td>\
+      </tr>\
+      <tr>\
+        <td>Changes in a specific project</td>\
+        <td>\
+          <code>\
+            "Gerrit-Project: <em>project-name</em>"\
+          </code>\
+        </td>\
+      </tr>\
+      <tr>\
+        <td>Messages related to a specific Change ID</td>\
+        <td>\
+          <code>\
+            "Gerrit-Change-Id: <em>Change ID</em>"\
+          </code>\
+        </td>\
+      </tr>\
+      <tr>\
+        <td>Messages related to a specific change number</td>\
+        <td>\
+          <code>\
+            "Gerrit-Change-Number: <em>change number</em>"\
+          </code>\
+        </td>\
+      </tr>\
+    </tbody>\
+  </table>
 
 newAgreement = New Contributor Agreement
 agreementName = Name
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
index cbd7635..a537063 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
@@ -21,6 +21,7 @@
 import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.rpc.NativeString;
 import com.google.gerrit.client.rpc.Natives;
+import com.google.gerrit.client.ui.ComplexDisclosurePanel;
 import com.google.gerrit.client.ui.OnEditEnabler;
 import com.google.gerrit.common.PageLinks;
 import com.google.gerrit.common.errors.EmailException;
@@ -153,6 +154,11 @@
           }
         });
 
+    final ComplexDisclosurePanel mailFilterHelp =
+        new ComplexDisclosurePanel(Util.C.emailFilterHelpTitle(), false);
+    mailFilterHelp.setContent(new HTML(Util.C.emailFilterHelp()));
+    body.add(mailFilterHelp);
+
     emailPick.addChangeHandler(
         new ChangeHandler() {
           @Override
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooter.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooter.soy
index a034872..37ac126 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooter.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooter.soy
@@ -30,7 +30,8 @@
   {/if}
 
   {if $email.settingsUrl}
-    To unsubscribe, visit {$email.settingsUrl}{\n}
+    To unsubscribe, or for help writing mail filters,{sp}
+    visit {$email.settingsUrl}{\n}
   {/if}
 
   {if $email.changeUrl or $email.settingsUrl}
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
index 61feb57..00f21db 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeFooterHtml.soy
@@ -29,7 +29,8 @@
       {/if}
       {if $email.changeUrl and $email.settingsUrl}{sp}{/if}
       {if $email.settingsUrl}
-        To unsubscribe, visit <a href="{$email.settingsUrl}">settings</a>.
+        To unsubscribe, or for help writing mail filters,{sp}
+        visit <a href="{$email.settingsUrl}">settings</a>.
       {/if}
     </p>
   {/if}
diff --git a/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior.html b/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior.html
new file mode 100644
index 0000000..4074c3b
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior.html
@@ -0,0 +1,63 @@
+<!--
+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="../base-url-behavior/base-url-behavior.html">
+<script>
+(function(window) {
+  'use strict';
+
+  const PROBE_PATH = '/Documentation/index.html';
+  const DOCS_BASE_PATH = '/Documentation';
+
+  let cachedPromise;
+
+  /** @polymerBehavior Gerrit.DocsUrlBehavior */
+  const DocsUrlBehavior = {
+
+    /**
+     * Get the docs base URL from either the server config or by probing.
+     * @param {Object} config The server config.
+     * @param {!Object} restApi A REST API instance
+     * @return {!Promise<String>} A promise that resolves with the docs base
+     *     URL.
+     */
+    getDocsBaseUrl(config, restApi) {
+      if (!cachedPromise) {
+        cachedPromise = new Promise(resolve => {
+          if (config && config.gerrit && config.gerrit.doc_url) {
+            resolve(config.gerrit.doc_url);
+          } else {
+            restApi.probePath(this.getBaseUrl() + PROBE_PATH).then(ok => {
+              resolve(ok ? (this.getBaseUrl() + DOCS_BASE_PATH) : null);
+            });
+          }
+        });
+      }
+      return cachedPromise;
+    },
+
+    /** For testing only. */
+    _clearDocsBaseUrlCache() {
+      cachedPromise = undefined;
+    },
+  };
+
+  window.Gerrit = window.Gerrit || {};
+  window.Gerrit.DocsUrlBehavior = [
+    window.Gerrit.BaseUrlBehavior,
+    DocsUrlBehavior,
+  ];
+})(window);
+</script>
diff --git a/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html b/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
new file mode 100644
index 0000000..8154c78
--- /dev/null
+++ b/polygerrit-ui/app/behaviors/docs-url-behavior/docs-url-behavior_test.html
@@ -0,0 +1,98 @@
+<!--
+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.
+-->
+<!-- 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>docs-url-behavior</title>
+
+<link rel="import" href="docs-url-behavior.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <docs-url-behavior-element></docs-url-behavior-element>
+  </template>
+</test-fixture>
+
+<script>
+  suite('docs-url-behavior tests', () => {
+    let element;
+
+    suiteSetup(() => {
+      // Define a Polymer element that uses this behavior.
+      Polymer({
+        is: 'docs-url-behavior-element',
+        behaviors: [Gerrit.DocsUrlBehavior],
+      });
+    });
+
+    setup(() => {
+      element = fixture('basic');
+      element._clearDocsBaseUrlCache();
+    });
+
+    test('null config', () => {
+      const mockRestApi = {
+        probePath: sinon.stub().returns(Promise.resolve(true)),
+      };
+      return element.getDocsBaseUrl(null, mockRestApi)
+          .then(docsBaseUrl => {
+            assert.isTrue(
+                mockRestApi.probePath.calledWith('/Documentation/index.html'));
+            assert.equal(docsBaseUrl, '/Documentation');
+          });
+    });
+
+    test('no doc config', () => {
+      const mockRestApi = {
+        probePath: sinon.stub().returns(Promise.resolve(true)),
+      };
+      const config = {gerrit: {}};
+      return element.getDocsBaseUrl(config, mockRestApi)
+          .then(docsBaseUrl => {
+            assert.isTrue(
+                mockRestApi.probePath.calledWith('/Documentation/index.html'));
+            assert.equal(docsBaseUrl, '/Documentation');
+          });
+    });
+
+    test('has doc config', () => {
+      const mockRestApi = {
+        probePath: sinon.stub().returns(Promise.resolve(true)),
+      };
+      const config = {gerrit: {doc_url: 'foobar'}};
+      return element.getDocsBaseUrl(config, mockRestApi)
+          .then(docsBaseUrl => {
+            assert.isFalse(mockRestApi.probePath.called);
+            assert.equal(docsBaseUrl, 'foobar');
+          });
+    });
+
+    test('no probe', () => {
+      const mockRestApi = {
+        probePath: sinon.stub().returns(Promise.resolve(false)),
+      };
+      return element.getDocsBaseUrl(null, mockRestApi)
+          .then(docsBaseUrl => {
+            assert.isTrue(
+                mockRestApi.probePath.calledWith('/Documentation/index.html'));
+            assert.isNotOk(docsBaseUrl);
+          });
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
index 258c901..683beb6 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -15,6 +15,7 @@
 -->
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/docs-url-behavior/docs-url-behavior.html">
 <link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
 <link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
index 620f733..b065000 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.js
@@ -103,6 +103,7 @@
 
     behaviors: [
       Gerrit.BaseUrlBehavior,
+      Gerrit.DocsUrlBehavior,
     ],
 
     observers: [
@@ -188,24 +189,9 @@
     },
 
     _loadConfig() {
-      this.$.restAPI.getConfig().then(config => {
-        if (config && config.gerrit && config.gerrit.doc_url) {
-          this._docBaseUrl = config.gerrit.doc_url;
-        }
-        if (!this._docBaseUrl) {
-          return this._probeDocLink('/Documentation/index.html');
-        }
-      });
-    },
-
-    _probeDocLink(path) {
-      return this.$.restAPI.probePath(this.getBaseUrl() + path).then(ok => {
-        if (ok) {
-          this._docBaseUrl = this.getBaseUrl() + '/Documentation';
-        } else {
-          this._docBaseUrl = null;
-        }
-      });
+      this.$.restAPI.getConfig()
+          .then(config => this.getDocsBaseUrl(config, this.$.restAPI))
+          .then(docBaseUrl => { this._docBaseUrl = docBaseUrl; });
     },
 
     _accountLoaded(account) {
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
index 0536452..96f84fd 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
@@ -16,6 +16,7 @@
 
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
 
+<link rel="import" href="../../../behaviors/docs-url-behavior/docs-url-behavior.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../../styles/gr-menu-page-styles.html">
 <link rel="import" href="../../../styles/gr-page-nav-styles.html">
@@ -44,6 +45,12 @@
       #email {
         margin-bottom: 1em;
       }
+      .filters p {
+        margin-bottom: 1em;
+      }
+      .queryExample em {
+        color: violet;
+      }
     </style>
     <style include="gr-form-styles"></style>
     <style include="gr-menu-page-styles"></style>
@@ -69,6 +76,7 @@
               <a href="#Agreements">Agreements</a>
             </li>
           </template>
+          <li><a href="#MailFilters">Mail Filters</a></li>
         </ul>
       </gr-page-nav>
       <main class="gr-form-styles">
@@ -381,6 +389,76 @@
             <gr-agreements-list id="agreementsList"></gr-agreements-list>
           </fieldset>
         </template>
+        <h2 id="MailFilters">Mail Filters</h2>
+        <fieldset class="filters">
+          <p>
+            Gerrit emails include metadata about the change to support
+            writing mail filters.
+          </p>
+          <p>
+            Here are some example Gmail queries that can be used for filters or
+            for searching through archived messages. View the
+            <a href$="[[_getFilterDocsLink(_docsBaseUrl)]]"
+                target="_blank"
+                rel="nofollow">Gerrit documentation</a>
+            for the complete set of footers.
+          </p>
+          <table>
+            <tbody>
+              <tr><th>Name</th><th>Query</th></tr>
+              <tr>
+                <td>Changes requesting my review</td>
+                <td>
+                  <code class="queryExample">
+                    "Gerrit-Reviewer: <em>Your Name</em>
+                    &lt;<em>your.email@example.com</em>&gt;"
+                  </code>
+                </td>
+              </tr>
+              <tr>
+                <td>Changes from a specific owner</td>
+                <td>
+                  <code class="queryExample">
+                    "Gerrit-Owner: <em>Owner name</em>
+                    &lt;<em>owner.email@example.com</em>&gt;"
+                  </code>
+                </td>
+              </tr>
+              <tr>
+                <td>Changes targeting a specific branch</td>
+                <td>
+                  <code class="queryExample">
+                    "Gerrit-Branch: <em>branch-name</em>"
+                  </code>
+                </td>
+              </tr>
+              <tr>
+                <td>Changes in a specific project</td>
+                <td>
+                  <code class="queryExample">
+                    "Gerrit-Project: <em>project-name</em>"
+                  </code>
+                </td>
+              </tr>
+              <tr>
+                <td>Messages related to a specific Change ID</td>
+                <td>
+                  <code class="queryExample">
+                    "Gerrit-Change-Id: <em>Change ID</em>"
+                  </code>
+                </td>
+              </tr>
+              <tr>
+                <td>Messages related to a specific change number</td>
+                <td>
+                  <code class="queryExample">
+                    "Gerrit-Change-Number: <em>change number</em>"
+                  </code>
+                </td>
+              </tr>
+            </tbody>
+          </table>
+        </fieldset>
       </main>
     </div>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
index 1e5d61a..a820403 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
@@ -25,6 +25,10 @@
     'email_format',
   ];
 
+  const GERRIT_DOCS_BASE_URL = 'https://gerrit-review.googlesource.com/' +
+      'Documentation';
+  const GERRIT_DOCS_FILTER_PATH = '/user-notify.html';
+
   Polymer({
     is: 'gr-settings-view',
 
@@ -103,6 +107,7 @@
         value: null,
       },
       _serverConfig: Object,
+      _docsBaseUrl: String,
 
       /**
        * For testing purposes.
@@ -111,6 +116,7 @@
     },
 
     behaviors: [
+      Gerrit.DocsUrlBehavior,
       Gerrit.ChangeTableBehavior,
     ],
 
@@ -144,9 +150,17 @@
 
       promises.push(this.$.restAPI.getConfig().then(config => {
         this._serverConfig = config;
+        const configPromises = [];
+
         if (this._serverConfig.sshd) {
-          return this.$.sshEditor.loadData();
+          configPromises.push(this.$.sshEditor.loadData());
         }
+
+        configPromises.push(
+            this.getDocsBaseUrl(config, this.$.restAPI)
+                .then(baseUrl => { this._docsBaseUrl = baseUrl; }));
+
+        return Promise.all(configPromises);
       }));
 
       if (this.params.emailToken) {
@@ -339,5 +353,13 @@
         this._newEmail = '';
       });
     },
+
+    _getFilterDocsLink(docsBaseUrl) {
+      let base = docsBaseUrl;
+      if (!base || !base.startsWith('http')) {
+        base = GERRIT_DOCS_BASE_URL;
+      }
+      return base + GERRIT_DOCS_FILTER_PATH;
+    },
   });
 })();
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index da4ad2e..f7f11b5 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -140,6 +140,7 @@
   // Behaviors tests.
   const behaviors = [
     'base-url-behavior/base-url-behavior_test.html',
+    'docs-url-behavior/docs-url-behavior_test.html',
     'keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html',
     'rest-client-behavior/rest-client-behavior_test.html',
     'gr-change-table-behavior/gr-change-table-behavior_test.html',