Merge branch 'main' into avm99963-monorail

Merged commit 34d8229ae2b51fb1a15bd208e6fe6185c94f6266

GitOrigin-RevId: 7ee0917f93a577e475f8e09526dd144d245593f4
diff --git a/static_src/reducers/base.js b/static_src/reducers/base.js
index f4603b7..9035123 100644
--- a/static_src/reducers/base.js
+++ b/static_src/reducers/base.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/hotlists.js b/static_src/reducers/hotlists.js
index 95989cc..a46bbfa 100644
--- a/static_src/reducers/hotlists.js
+++ b/static_src/reducers/hotlists.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/hotlists.test.js b/static_src/reducers/hotlists.test.js
index 4aa42a2..d9a68c9 100644
--- a/static_src/reducers/hotlists.test.js
+++ b/static_src/reducers/hotlists.test.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/issueV0.js b/static_src/reducers/issueV0.js
index 8f670c9..5ffafd9 100644
--- a/static_src/reducers/issueV0.js
+++ b/static_src/reducers/issueV0.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -30,6 +30,13 @@
 
 /** @typedef {import('redux').AnyAction} AnyAction */
 
+
+const RESTRICT_VIEW_PREFIX = 'restrict-view-';
+const RESTRICT_EDIT_PREFIX = 'restrict-editissue-';
+const RESTRICT_COMMENT_PREFIX = 'restrict-addissuecomment-';
+const MIGRATED_BUGANIZER_ISSUE_PREFIXES = ['migrated-to-b-', 'copybara-migration-complete-', 'cob-migrated-to-b-'];
+const MIGRATED_LAUNCH_ISSUE_PREFIXES = ['migrated-to-launch-'];
+
 // Actions
 export const VIEW_ISSUE = 'VIEW_ISSUE';
 
@@ -490,12 +497,6 @@
 });
 
 // Selectors
-const RESTRICT_VIEW_PREFIX = 'restrict-view-';
-const RESTRICT_EDIT_PREFIX = 'restrict-editissue-';
-const RESTRICT_COMMENT_PREFIX = 'restrict-addissuecomment-';
-const MIGRATED_ISSUE_PREFIX = 'migrated-to-';
-const MIGRATED_BUGANIZER_ISSUE_PREFIX = 'migrated-to-b-';
-const MIGRATED_LAUNCH_ISSUE_PREFIX = 'migrated-to-launch-';
 
 /**
  * Selector to retrieve all normalized Issue data in the Redux store,
@@ -706,48 +707,60 @@
     },
 );
 
+/**
+ * Helper to find the issue ID for a migration label if one exists, otherwise
+ * returns undefined.
+ * @param {Array<LabelRef>} labelRefs
+ * @param {Array<string>} validPrefixes
+ * @return {string?} issue referenced in label or undefined if none found.
+ */
+function extractIdFromLabels(labelRefs, validPrefixes) {
+  // Assume that there's only one migrated-to-* label. Or at least drop any
+  // labels besides the first one.
+  const migrationLabel = labelRefs.find((labelRef) => validPrefixes.find(
+    (prefix) => labelRef.label.toLowerCase().startsWith(prefix)));
+
+  if (!migrationLabel) return undefined;
+
+  const {label} = migrationLabel;
+
+  const matchedPrefix = validPrefixes.find((prefix) => label.startsWith(prefix));
+
+  return migrationLabel.label.substring(matchedPrefix.length);
+}
+
 // Gets the Issue Tracker or Launch ID of a moved issue.
 export const migratedId = createSelector(
+  viewedIssue,
   labelRefs,
-  (labelRefs) => {
+  (issue, labelRefs) => {
+    if (issue && issue.migratedId) return issue.migratedId;
     if (!labelRefs) return '';
 
-    // Assume that there's only one migrated-to-* label. Or at least drop any
-    // labels besides the first one.
-    const migrationLabel = labelRefs.find((labelRef) => {
-      return labelRef.label.toLowerCase().startsWith(MIGRATED_ISSUE_PREFIX);
-    });
-    
-    if (migrationLabel) {
-      if (migrationLabel.label.toLowerCase().startsWith(MIGRATED_BUGANIZER_ISSUE_PREFIX)) {
-        return migrationLabel.label.substring(MIGRATED_BUGANIZER_ISSUE_PREFIX.length);
-      } else if (migrationLabel.label.toLowerCase().startsWith(MIGRATED_LAUNCH_ISSUE_PREFIX)) {
-        return migrationLabel.label.substring(MIGRATED_LAUNCH_ISSUE_PREFIX.length);
-      }
-    }
+    const launchIssue = extractIdFromLabels(labelRefs, MIGRATED_LAUNCH_ISSUE_PREFIXES);
+    if (launchIssue) return launchIssue;
+
+    const bIssue = extractIdFromLabels(labelRefs, MIGRATED_BUGANIZER_ISSUE_PREFIXES);
+    if (bIssue) return bIssue;
+
     return '';
   },
 );
 
 // Gets the Issue Migrated Type of a moved issue.
 export const migratedType = createSelector(
+  viewedIssue,
   labelRefs,
-  (labelRefs) => {
+  (issue, labelRefs) => {
+    if (issue && issue.migratedId) return migratedTypes.BUGANIZER_TYPE;
     if (!labelRefs) return migratedTypes.NONE;
 
-    // Assume that there's only one migrated-to-* label. Or at least drop any
-    // labels besides the first one.
-    const migrationLabel = labelRefs.find((labelRef) => {
-      return labelRef.label.toLowerCase().startsWith(MIGRATED_ISSUE_PREFIX);
-    });
+    const launchIssue = extractIdFromLabels(labelRefs, MIGRATED_LAUNCH_ISSUE_PREFIXES);
+    if (launchIssue) return migratedTypes.LAUNCH_TYPE;
 
-    if (migrationLabel) {
-      if (migrationLabel.label.toLowerCase().startsWith(MIGRATED_BUGANIZER_ISSUE_PREFIX)) {
-        return migratedTypes.BUGANIZER_TYPE;
-      } else if (migrationLabel.label.toLowerCase().startsWith(MIGRATED_LAUNCH_ISSUE_PREFIX)) {
-        return migratedTypes.LAUNCH_TYPE;
-      }
-    }
+    const bIssue = extractIdFromLabels(labelRefs, MIGRATED_BUGANIZER_ISSUE_PREFIXES);
+    if (bIssue) return migratedTypes.BUGANIZER_TYPE;
+
     return migratedTypes.NONE;
   },
 );
diff --git a/static_src/reducers/issueV0.test.js b/static_src/reducers/issueV0.test.js
index b79cdb5..9e9a0b1 100644
--- a/static_src/reducers/issueV0.test.js
+++ b/static_src/reducers/issueV0.test.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -300,72 +300,103 @@
     });
   });
 
-  it('migratedId', () => {
-    assert.equal(issueV0.migratedId(wrapIssue()), '');
-    assert.equal(issueV0.migratedId(wrapIssue({labelRefs: []})), '');
+  describe('migratedId', () => {
+    it('no id on empty labels', () => {
+      assert.equal(issueV0.migratedId(wrapIssue()), '');
+      assert.equal(issueV0.migratedId(wrapIssue({labelRefs: []})), '');
+    });
 
-    assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
-      {label: 'IgnoreThis'},
-      {label: 'IgnoreThis2'},
-    ]})), '');
+    it('ignores irrelevant labels', () => {
+      assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
+        {label: 'IgnoreThis'},
+        {label: 'IgnoreThis2'},
+      ]})), '');
 
-    assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
-      {label: 'IgnoreThis'},
-      {label: 'IgnoreThis2'},
-      {label: 'migrated-to-b-6789'},
-    ]})), '6789');
+      assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
+        {label: 'IgnoreThis'},
+        {label: 'IgnoreThis2'},
+        {label: 'migrated-to-b-6789'},
+      ]})), '6789');
+    });
 
-    assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
-      {label: 'migrated-to-b-1234'},
-    ]})), '1234');
+    it('finds first relevant label', () => {
+      assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
+        {label: 'migrated-to-b-1234'},
+      ]})), '1234');
 
-    // We assume there's only one migrated-to-b-* label.
-    assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
-      {label: 'migrated-to-b-1234'},
-      {label: 'migrated-to-b-6789'},
-    ]})), '1234');
+      // We assume there's only one migrated-to-b-* label.
+      assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
+        {label: 'migrated-to-b-1234'},
+        {label: 'migrated-to-b-6789'},
+      ]})), '1234');
+    });
 
-    assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
-      {label: 'IgnoreThis'},
-      {label: 'IgnoreThis2'},
-      {label: 'migrated-to-launch-6789'},
-    ]})), '6789');
+    it('finds copybara labels', () => {
+      assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
+        {label: 'copybara-migration-complete-1234'},
+      ]})), '1234');
 
-    assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
-      {label: 'migrated-to-launch-1234'},
-    ]})), '1234');
+      assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
+        {label: 'copybara-migration-complete-1234'},
+        {label: 'migrated-to-b-6789'},
+      ]})), '1234');
+    });
 
-    // We assume there's only one migrated-to-* label.
-    assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
-      {label: 'migrated-to-launch-1234'},
-      {label: 'migrated-to-b-6789'},
-    ]})), '1234');
+    it('finds launch labels', () => {
+      assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
+        {label: 'IgnoreThis'},
+        {label: 'IgnoreThis2'},
+        {label: 'migrated-to-launch-6789'},
+      ]})), '6789');
+
+      assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
+        {label: 'migrated-to-launch-1234'},
+      ]})), '1234');
+
+      assert.equal(issueV0.migratedId(wrapIssue({labelRefs: [
+        {label: 'migrated-to-launch-1234'},
+        {label: 'migrated-to-b-6789'},
+      ]})), '1234');
+    });
   });
 
-  it('migratedType', () => {
-    assert.equal(issueV0.migratedType(wrapIssue()), migratedTypes.NONE);
-    assert.equal(issueV0.migratedType(wrapIssue({labelRefs: []})), migratedTypes.NONE);
+ describe('migratedType', () => {
+    it('none type on empty labels', () => {
+      assert.equal(issueV0.migratedType(wrapIssue()), migratedTypes.NONE);
+      assert.equal(issueV0.migratedType(wrapIssue({labelRefs: []})), migratedTypes.NONE);
+    });
 
-    assert.equal(issueV0.migratedType(wrapIssue({labelRefs: [
-      {label: 'IgnoreThis'},
-      {label: 'IgnoreThis2'},
-    ]})), migratedTypes.NONE);
+    it('none type on irrelevant labels', () => {
+      assert.equal(issueV0.migratedType(wrapIssue({labelRefs: [
+        {label: 'IgnoreThis'},
+        {label: 'IgnoreThis2'},
+      ]})), migratedTypes.NONE);
+    });
 
-    assert.equal(issueV0.migratedType(wrapIssue({labelRefs: [
-      {label: 'IgnoreThis'},
-      {label: 'IgnoreThis2'},
-      {label: 'migrated-to-b-6789'},
-    ]})), migratedTypes.BUGANIZER_TYPE);
+    it('buganizer type for buganizer labels', () => {
+      assert.equal(issueV0.migratedType(wrapIssue({labelRefs: [
+        {label: 'IgnoreThis'},
+        {label: 'IgnoreThis2'},
+        {label: 'migrated-to-b-6789'},
+      ]})), migratedTypes.BUGANIZER_TYPE);
 
-    assert.equal(issueV0.migratedType(wrapIssue({labelRefs: [
-      {label: 'migrated-to-launch-1234'},
-    ]})), migratedTypes.LAUNCH_TYPE);
+      assert.equal(issueV0.migratedType(wrapIssue({labelRefs: [
+        {label: 'IgnoreThis'},
+        {label: 'copybara-migration-complete-1234'},
+      ]})), migratedTypes.BUGANIZER_TYPE);
+    });
 
-    // We assume there's only one migrated-to-b-* label.
-    assert.equal(issueV0.migratedType(wrapIssue({labelRefs: [
-      {label: 'migrated-to-launch-1234'},
-      {label: 'migrated-to-b-6789'},
-    ]})), migratedTypes.LAUNCH_TYPE);
+    it('launch type for launch labels', () => {
+      assert.equal(issueV0.migratedType(wrapIssue({labelRefs: [
+        {label: 'migrated-to-launch-1234'},
+      ]})), migratedTypes.LAUNCH_TYPE);
+
+      // We assume there's only one migrated-to-b-* label.
+      assert.equal(issueV0.migratedType(wrapIssue({labelRefs: [
+        {label: 'migrated-to-launch-1234'},
+        {label: 'migrated-to-b-6789'},
+      ]})), migratedTypes.LAUNCH_TYPE);
+    });
   });
 
 
diff --git a/static_src/reducers/permissions.js b/static_src/reducers/permissions.js
index 2f0101b..3809f9f 100644
--- a/static_src/reducers/permissions.js
+++ b/static_src/reducers/permissions.js
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/permissions.test.js b/static_src/reducers/permissions.test.js
index 3c29076..2d63308 100644
--- a/static_src/reducers/permissions.test.js
+++ b/static_src/reducers/permissions.test.js
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/projectV0.js b/static_src/reducers/projectV0.js
index 5101ff8..c8bd80a 100644
--- a/static_src/reducers/projectV0.js
+++ b/static_src/reducers/projectV0.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/projectV0.test.js b/static_src/reducers/projectV0.test.js
index fb1f051..772e29d 100644
--- a/static_src/reducers/projectV0.test.js
+++ b/static_src/reducers/projectV0.test.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/projects.js b/static_src/reducers/projects.js
index 955dfea..f9ab04c 100644
--- a/static_src/reducers/projects.js
+++ b/static_src/reducers/projects.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/projects.test.js b/static_src/reducers/projects.test.js
index 0a9dee4..5b2bab5 100644
--- a/static_src/reducers/projects.test.js
+++ b/static_src/reducers/projects.test.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/redux-helpers.js b/static_src/reducers/redux-helpers.js
index ce80d60..2a6964a 100644
--- a/static_src/reducers/redux-helpers.js
+++ b/static_src/reducers/redux-helpers.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/redux-helpers.test.js b/static_src/reducers/redux-helpers.test.js
index 93f0e0a..eb9105d 100644
--- a/static_src/reducers/redux-helpers.test.js
+++ b/static_src/reducers/redux-helpers.test.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/sitewide.js b/static_src/reducers/sitewide.js
index f7e20d7..e4ee775 100644
--- a/static_src/reducers/sitewide.js
+++ b/static_src/reducers/sitewide.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/sitewide.test.js b/static_src/reducers/sitewide.test.js
index 114ecaf..401cee2 100644
--- a/static_src/reducers/sitewide.test.js
+++ b/static_src/reducers/sitewide.test.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/stars.js b/static_src/reducers/stars.js
index b67ff9d..051d99a 100644
--- a/static_src/reducers/stars.js
+++ b/static_src/reducers/stars.js
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/stars.test.js b/static_src/reducers/stars.test.js
index 3437723..14f234f 100644
--- a/static_src/reducers/stars.test.js
+++ b/static_src/reducers/stars.test.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/ui.js b/static_src/reducers/ui.js
index 871cf87..7ebc1f6 100644
--- a/static_src/reducers/ui.js
+++ b/static_src/reducers/ui.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/ui.test.js b/static_src/reducers/ui.test.js
index 587bc0c..c110e16 100644
--- a/static_src/reducers/ui.test.js
+++ b/static_src/reducers/ui.test.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/userV0.js b/static_src/reducers/userV0.js
index 33252c5..e561ad9 100644
--- a/static_src/reducers/userV0.js
+++ b/static_src/reducers/userV0.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/userV0.test.js b/static_src/reducers/userV0.test.js
index aab7989..30cc311 100644
--- a/static_src/reducers/userV0.test.js
+++ b/static_src/reducers/userV0.test.js
@@ -1,4 +1,4 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
+// Copyright 2019 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/users.js b/static_src/reducers/users.js
index af8609c..f8a0306 100644
--- a/static_src/reducers/users.js
+++ b/static_src/reducers/users.js
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
diff --git a/static_src/reducers/users.test.js b/static_src/reducers/users.test.js
index ea0ce61..fe02b33 100644
--- a/static_src/reducers/users.test.js
+++ b/static_src/reducers/users.test.js
@@ -1,4 +1,4 @@
-// Copyright 2020 The Chromium Authors. All rights reserved.
+// Copyright 2020 The Chromium Authors
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.