Derive patchRange directly from diff view model

Use `patchNum` and `basePatchNum` directly instead of using a
`patchRange` wrapper.

Remove `initPatchRange()` method. Subscribe to view model instead.

The goal is to pick apart the `viewStateChanged()` method one by one.
Relying on that method to initialize and update correctly has proven
to be extremely error prone. Each piece of data should have a more
direct way of how it is initialized or updated.

The patchset range is maintained by the view model already. Let's
just subscribe to it.

Release-Notes: skip
Change-Id: I21ddf5ce22f6d233bcaf5504c42d236a2812e24a
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
index ac1c1c8..4cd40d7 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.ts
@@ -172,7 +172,21 @@
 
   // Private but used in tests.
   @state()
-  patchRange?: PatchRange;
+  get patchRange(): PatchRange | undefined {
+    if (!this.patchNum) return undefined;
+    return {
+      patchNum: this.patchNum,
+      basePatchNum: this.basePatchNum,
+    };
+  }
+
+  // Private but used in tests.
+  @state()
+  patchNum?: RevisionPatchSetNum;
+
+  // Private but used in tests.
+  @state()
+  basePatchNum: BasePatchSetNum = PARENT;
 
   // Private but used in tests.
   @state()
@@ -448,6 +462,16 @@
     );
     subscribe(
       this,
+      () => this.getViewModel().patchNum$,
+      patchNum => (this.patchNum = patchNum)
+    );
+    subscribe(
+      this,
+      () => this.getViewModel().basePatchNum$,
+      basePatchNum => (this.basePatchNum = basePatchNum ?? PARENT)
+    );
+    subscribe(
+      this,
       () =>
         combineLatest([
           this.getViewModel().diffPath$,
@@ -722,7 +746,8 @@
     if (
       changedProperties.has('changeComments') ||
       changedProperties.has('path') ||
-      changedProperties.has('patchRange') ||
+      changedProperties.has('patchNum') ||
+      changedProperties.has('basePatchNum') ||
       changedProperties.has('files')
     ) {
       if (this.changeComments && this.path && this.patchRange) {
@@ -744,7 +769,7 @@
 
   override render() {
     if (this.viewState?.childView !== ChangeChildView.DIFF) return nothing;
-    if (!this.patchRange) return nothing;
+    if (!this.patchNum) return nothing;
     if (!this.changeNum) return nothing;
     if (!this.path) return nothing;
     const file = this.getFileRange();
@@ -777,7 +802,7 @@
 
   private renderStickyHeader() {
     return html` <div
-      class="stickyHeader ${this.computeEditMode() ? 'editMode' : ''}"
+      class="stickyHeader ${this.patchNum === EDIT ? 'editMode' : ''}"
     >
       <h1 class="assistive-tech-only">
         Diff of ${this.path ? computeTruncatedPath(this.path) : ''}
@@ -872,8 +897,8 @@
       <gr-patch-range-select
         id="rangeSelect"
         .changeNum=${this.changeNum}
-        .patchNum=${this.patchRange?.patchNum}
-        .basePatchNum=${this.patchRange?.basePatchNum}
+        .patchNum=${this.patchNum}
+        .basePatchNum=${this.basePatchNum}
         .filesWeblinks=${this.filesWeblinks}
         .availablePatches=${this.allPatchSets}
         .revisions=${this.change?.revisions}
@@ -1004,7 +1029,7 @@
         <gr-download-dialog
           id="downloadDialog"
           .change=${this.change}
-          .patchNum=${this.patchRange?.patchNum}
+          .patchNum=${this.patchNum}
           .config=${this.serverConfig?.download}
           @close=${this.handleDownloadDialogClose}
         ></gr-download-dialog>
@@ -1043,9 +1068,9 @@
   // Private but used in tests.
   setReviewed(
     reviewed: boolean,
-    patchNum: RevisionPatchSetNum | undefined = this.patchRange?.patchNum
+    patchNum: RevisionPatchSetNum | undefined = this.patchNum
   ) {
-    if (this.computeEditMode()) return;
+    if (this.patchNum === EDIT) return;
     if (!patchNum || !this.path || !this.changeNum) return;
     // if file is already reviewed then do not make a saveReview request
     if (this.reviewedFiles.has(this.path) && reviewed) return;
@@ -1300,7 +1325,6 @@
 
   private goToEditFile() {
     assertIsDefined(this.path, 'path');
-    assertIsDefined(this.patchRange, 'patchRange');
 
     // TODO(taoalpha): add a shortcut for editing
     const cursorAddress = this.cursor?.getAddress();
@@ -1354,14 +1378,14 @@
 
   private updateUrlToDiffUrl(lineNum?: number, leftSide?: boolean) {
     if (!this.change) return;
-    if (!this.patchRange) return;
+    if (!this.patchNum) return;
     if (!this.changeNum) return;
     if (!this.path) return;
     const url = createDiffUrl({
       changeNum: this.changeNum,
       repo: this.change.project,
-      patchNum: this.patchRange.patchNum,
-      basePatchNum: this.patchRange.basePatchNum,
+      patchNum: this.patchNum,
+      basePatchNum: this.basePatchNum,
       diffView: {
         path: this.path,
         lineNum,
@@ -1371,22 +1395,12 @@
     history.replaceState(null, '', url);
   }
 
-  // Private but used in tests.
-  initPatchRange() {
-    if (!this.change) return;
-    if (this.viewState?.childView !== ChangeChildView.DIFF) return;
-    if (this.viewState.patchNum) {
-      this.patchRange = {
-        patchNum: this.viewState.patchNum,
-        basePatchNum: this.viewState.basePatchNum || PARENT,
-      };
-    }
-  }
-
+  // TODO: This is probably not working anymore as expected, because we don't
+  // know in which order the view model subscriptions fire.
   private isSameDiffLoaded(value: ChangeViewState) {
     return (
-      this.patchRange?.basePatchNum === value.basePatchNum &&
-      this.patchRange?.patchNum === value.patchNum &&
+      this.basePatchNum === value.basePatchNum &&
+      this.patchNum === value.patchNum &&
       this.path === value.diffView?.path
     );
   }
@@ -1438,8 +1452,6 @@
       return;
     }
 
-    this.patchRange = undefined;
-
     this.changeNum = viewState.changeNum;
     this.classList.remove('hideComments');
 
@@ -1468,7 +1480,6 @@
     return Promise.all(promises)
       .then(() => {
         this.loading = false;
-        this.initPatchRange();
         return this.updateComplete.then(() => this.diffHost!.reload(true));
       })
       .then(() => {
@@ -1550,13 +1561,10 @@
   // Private but used in tests.
   handlePatchChange(e: CustomEvent) {
     if (!this.path) return;
-    if (!this.patchRange) return;
+    if (!this.patchNum) return;
 
     const {basePatchNum, patchNum} = e.detail;
-    if (
-      basePatchNum === this.patchRange.basePatchNum &&
-      patchNum === this.patchRange.patchNum
-    ) {
+    if (basePatchNum === this.basePatchNum && patchNum === this.patchNum) {
       return;
     }
     this.getChangeModel().navigateToDiff(
@@ -1587,7 +1595,7 @@
   computeDownloadDropdownLinks() {
     if (!this.change?.project) return [];
     if (!this.changeNum) return [];
-    if (!this.patchRange?.patchNum) return [];
+    if (!this.patchRange) return [];
     if (!this.path) return [];
 
     const links = [
@@ -1635,6 +1643,7 @@
     return links;
   }
 
+  // TODO: Move to view-model or router.
   // Private but used in tests.
   computeDownloadFileLink(
     repo: RepoName,
@@ -1663,6 +1672,7 @@
     return url;
   }
 
+  // TODO: Move to view-model or router.
   // Private but used in tests.
   computeDownloadPatchLink(
     repo: RepoName,
@@ -1692,11 +1702,6 @@
   }
 
   // Private but used in tests.
-  computeEditMode() {
-    return this.patchRange?.patchNum === EDIT;
-  }
-
-  // Private but used in tests.
   loadBlame() {
     this.isBlameLoading = true;
     fireAlert(this, LOADING_BLAME);
@@ -1737,15 +1742,15 @@
   // Private but used in tests.
   handleDiffAgainstBase() {
     assertIsDefined(this.path, 'path');
-    assertIsDefined(this.patchRange, 'patchRange');
+    assertIsDefined(this.patchNum, 'patchNum');
 
-    if (this.patchRange.basePatchNum === PARENT) {
+    if (this.basePatchNum === PARENT) {
       fireAlert(this, 'Base is already selected.');
       return;
     }
     this.getChangeModel().navigateToDiff(
       {path: this.path},
-      this.patchRange.patchNum,
+      this.patchNum,
       PARENT
     );
   }
@@ -1754,15 +1759,15 @@
   handleDiffBaseAgainstLeft() {
     if (this.viewState?.childView !== ChangeChildView.DIFF) return;
     assertIsDefined(this.path, 'path');
-    assertIsDefined(this.patchRange, 'patchRange');
+    assertIsDefined(this.patchNum, 'patchNum');
 
-    if (this.patchRange.basePatchNum === PARENT) {
+    if (this.basePatchNum === PARENT) {
       fireAlert(this, 'Left is already base.');
       return;
     }
     this.getChangeModel().navigateToDiff(
       {path: this.path},
-      this.patchRange.basePatchNum as RevisionPatchSetNum,
+      this.basePatchNum as RevisionPatchSetNum,
       PARENT
     );
   }
@@ -1770,9 +1775,9 @@
   // Private but used in tests.
   handleDiffAgainstLatest() {
     assertIsDefined(this.path, 'path');
-    assertIsDefined(this.patchRange, 'patchRange');
+    assertIsDefined(this.patchNum, 'patchNum');
 
-    if (this.patchRange.patchNum === this.latestPatchNum) {
+    if (this.patchNum === this.latestPatchNum) {
       fireAlert(this, 'Latest is already selected.');
       return;
     }
@@ -1780,16 +1785,16 @@
     this.getChangeModel().navigateToDiff(
       {path: this.path},
       this.latestPatchNum,
-      this.patchRange.basePatchNum
+      this.basePatchNum
     );
   }
 
   // Private but used in tests.
   handleDiffRightAgainstLatest() {
     assertIsDefined(this.path, 'path');
-    assertIsDefined(this.patchRange, 'patchRange');
+    assertIsDefined(this.patchNum, 'patchNum');
 
-    if (this.patchRange.patchNum === this.latestPatchNum) {
+    if (this.patchNum === this.latestPatchNum) {
       fireAlert(this, 'Right is already latest.');
       return;
     }
@@ -1797,19 +1802,16 @@
     this.getChangeModel().navigateToDiff(
       {path: this.path},
       this.latestPatchNum,
-      this.patchRange.patchNum as BasePatchSetNum
+      this.patchNum as BasePatchSetNum
     );
   }
 
   // Private but used in tests.
   handleDiffBaseAgainstLatest() {
     assertIsDefined(this.path, 'path');
-    assertIsDefined(this.patchRange, 'patchRange');
+    assertIsDefined(this.patchNum, 'patchNum');
 
-    if (
-      this.patchRange.patchNum === this.latestPatchNum &&
-      this.patchRange.basePatchNum === PARENT
-    ) {
+    if (this.patchNum === this.latestPatchNum && this.basePatchNum === PARENT) {
       fireAlert(this, 'Already diffing base against latest.');
       return;
     }
@@ -1849,11 +1851,11 @@
   private navigateToNextFileWithCommentThread() {
     if (!this.path) return;
     if (!this.files?.sortedPaths) return;
-    if (!this.patchRange) return;
+    const range = this.patchRange;
+    if (!range) return;
     if (!this.change) return;
     const hasComment = (path: string) =>
-      this.changeComments?.getCommentsForPath(path, this.patchRange!)?.length ??
-      0 > 0;
+      this.changeComments?.getCommentsForPath(path, range)?.length ?? 0 > 0;
     const filesWithComments = this.files.sortedPaths.filter(
       file => file === this.path || hasComment(file)
     );
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
index fc4157d..c35df68 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.ts
@@ -27,7 +27,6 @@
   createRevisions,
   createComment as createCommentGeneric,
   createDiff,
-  createPatchRange,
   createServerInfo,
   createConfig,
   createParsedChange,
@@ -42,7 +41,6 @@
   EDIT,
   NumericChangeId,
   PARENT,
-  PatchRange,
   PatchSetNum,
   PatchSetNumber,
   PathToCommentsInfoMap,
@@ -151,7 +149,8 @@
       getDiffRestApiStub = stubRestApi('getDiff');
       // Delayed in case a test updates element.diff.
       getDiffRestApiStub.callsFake(() => Promise.resolve(element.diff));
-      element.patchRange = createPatchRange();
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       element.changeComments = new ChangeComments({
         '/COMMIT_MSG': [
           createComment('c1', 10, 2, '/COMMIT_MSG'),
@@ -186,7 +185,6 @@
       const diffViewDisplayedStub = stubReporting('diffViewDisplayed');
       assertIsDefined(element.diffHost);
       sinon.stub(element.diffHost, 'reload').returns(Promise.resolve());
-      sinon.stub(element, 'initPatchRange');
       const viewStateChangedSpy = sinon.spy(element, 'viewStateChanged');
       element.viewState = {
         ...createDiffViewState(),
@@ -195,7 +193,8 @@
         diffView: {path: '/COMMIT_MSG'},
       };
       element.path = '/COMMIT_MSG';
-      element.patchRange = createPatchRange();
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       return viewStateChangedSpy.returnValues[0]?.then(() => {
         assert.isTrue(diffViewDisplayedStub.calledOnce);
       });
@@ -209,7 +208,6 @@
       assertIsDefined(element.diffHost);
       sinon.stub(element.diffHost, 'reload').returns(Promise.resolve());
       const viewStateChangedSpy = sinon.spy(element, 'viewStateChanged');
-      sinon.stub(element, 'initPatchRange');
       element.viewState = {
         ...createDiffViewState(),
         patchNum: 2 as RevisionPatchSetNum,
@@ -217,7 +215,8 @@
         diffView: {path: '/COMMIT_MSG'},
       };
       element.path = '/COMMIT_MSG';
-      element.patchRange = createPatchRange();
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       return viewStateChangedSpy.returnValues[0]!.then(() => {
         assert.isTrue(element.isBlameLoaded);
         assert.isTrue(loadBlameStub.calledOnce);
@@ -233,10 +232,8 @@
 
     test('renders', async () => {
       browserModel.setScreenWidth(0);
-      element.patchRange = {
-        basePatchNum: PARENT,
-        patchNum: 10 as RevisionPatchSetNum,
-      };
+      element.patchNum = 10 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       const change = {
         ...createParsedChange(),
         _number: 42 as NumericChangeId,
@@ -414,10 +411,8 @@
       clock = sinon.useFakeTimers();
       element.changeNum = 42 as NumericChangeId;
       browserModel.setScreenWidth(0);
-      element.patchRange = {
-        basePatchNum: PARENT,
-        patchNum: 10 as RevisionPatchSetNum,
-      };
+      element.patchNum = 10 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       element.change = {
         ...createParsedChange(),
         _number: 42 as NumericChangeId,
@@ -554,10 +549,8 @@
         'wheatley.md': [createComment('c2', 21, 10, 'wheatley.md')],
       };
       element.changeComments = new ChangeComments(comment);
-      element.patchRange = {
-        basePatchNum: PARENT,
-        patchNum: 10 as RevisionPatchSetNum,
-      };
+      element.patchNum = 10 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       element.change = {
         ...createParsedChange(),
         _number: 42 as NumericChangeId,
@@ -599,10 +592,8 @@
     });
 
     test('diff against base', async () => {
-      element.patchRange = {
-        basePatchNum: 5 as BasePatchSetNum,
-        patchNum: 10 as RevisionPatchSetNum,
-      };
+      element.patchNum = 10 as RevisionPatchSetNum;
+      element.basePatchNum = 5 as BasePatchSetNum;
       await element.updateComplete;
       element.handleDiffAgainstBase();
       const expected = [{path: 'some/path.txt'}, 10, PARENT];
@@ -612,10 +603,8 @@
     test('diff against latest', async () => {
       element.path = 'foo';
       element.latestPatchNum = 12 as PatchSetNumber;
-      element.patchRange = {
-        basePatchNum: 5 as BasePatchSetNum,
-        patchNum: 10 as RevisionPatchSetNum,
-      };
+      element.patchNum = 10 as RevisionPatchSetNum;
+      element.basePatchNum = 5 as BasePatchSetNum;
       await element.updateComplete;
       element.handleDiffAgainstLatest();
       const expected = [{path: 'foo'}, 12, 5];
@@ -625,10 +614,8 @@
     test('handleDiffBaseAgainstLeft', async () => {
       element.path = 'foo';
       element.latestPatchNum = 10 as PatchSetNumber;
-      element.patchRange = {
-        patchNum: 3 as RevisionPatchSetNum,
-        basePatchNum: 1 as BasePatchSetNum,
-      };
+      element.patchNum = 3 as RevisionPatchSetNum;
+      element.basePatchNum = 1 as BasePatchSetNum;
       element.viewState = {
         ...createDiffViewState(),
         patchNum: 3 as RevisionPatchSetNum,
@@ -644,10 +631,8 @@
     test('handleDiffRightAgainstLatest', async () => {
       element.path = 'foo';
       element.latestPatchNum = 10 as PatchSetNumber;
-      element.patchRange = {
-        basePatchNum: 1 as BasePatchSetNum,
-        patchNum: 3 as RevisionPatchSetNum,
-      };
+      element.patchNum = 3 as RevisionPatchSetNum;
+      element.basePatchNum = 1 as BasePatchSetNum;
       await element.updateComplete;
       element.handleDiffRightAgainstLatest();
       const expected = [{path: 'foo'}, 10, 3];
@@ -656,10 +641,8 @@
 
     test('handleDiffBaseAgainstLatest', async () => {
       element.latestPatchNum = 10 as PatchSetNumber;
-      element.patchRange = {
-        basePatchNum: 1 as BasePatchSetNum,
-        patchNum: 3 as RevisionPatchSetNum,
-      };
+      element.patchNum = 3 as RevisionPatchSetNum;
+      element.basePatchNum = 1 as BasePatchSetNum;
       await element.updateComplete;
       element.handleDiffBaseAgainstLatest();
       const expected = [{path: 'some/path.txt'}, 10, PARENT];
@@ -678,10 +661,8 @@
 
     test('A navigates to change with logged in', async () => {
       element.changeNum = 42 as NumericChangeId;
-      element.patchRange = {
-        basePatchNum: 5 as BasePatchSetNum,
-        patchNum: 10 as RevisionPatchSetNum,
-      };
+      element.patchNum = 10 as RevisionPatchSetNum;
+      element.basePatchNum = 5 as BasePatchSetNum;
       element.change = {
         ...createParsedChange(),
         _number: 42 as NumericChangeId,
@@ -706,10 +687,8 @@
 
     test('A navigates to change with old patch number with logged in', async () => {
       element.changeNum = 42 as NumericChangeId;
-      element.patchRange = {
-        basePatchNum: PARENT,
-        patchNum: 1 as RevisionPatchSetNum,
-      };
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       element.change = {
         ...createParsedChange(),
         _number: 42 as NumericChangeId,
@@ -730,10 +709,8 @@
 
     test('keyboard shortcuts with patch range', () => {
       element.changeNum = 42 as NumericChangeId;
-      element.patchRange = {
-        basePatchNum: 5 as BasePatchSetNum,
-        patchNum: 10 as RevisionPatchSetNum,
-      };
+      element.patchNum = 10 as RevisionPatchSetNum;
+      element.basePatchNum = 5 as BasePatchSetNum;
       element.change = {
         ...createParsedChange(),
         _number: 42 as NumericChangeId,
@@ -788,10 +765,8 @@
 
     test('keyboard shortcuts with old patch number', async () => {
       element.changeNum = 42 as NumericChangeId;
-      element.patchRange = {
-        basePatchNum: PARENT,
-        patchNum: 1 as RevisionPatchSetNum,
-      };
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       element.change = {
         ...createParsedChange(),
         _number: 42 as NumericChangeId,
@@ -837,10 +812,8 @@
     test('edit should redirect to edit page', async () => {
       element.loggedIn = true;
       element.path = 't.txt';
-      element.patchRange = {
-        basePatchNum: PARENT,
-        patchNum: 1 as RevisionPatchSetNum,
-      };
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       await element.updateComplete;
       const editBtn = queryAndAssert<GrButton>(
         element,
@@ -858,10 +831,8 @@
       const lineNumber = 42;
       element.loggedIn = true;
       element.path = 't.txt';
-      element.patchRange = {
-        basePatchNum: PARENT,
-        patchNum: 1 as RevisionPatchSetNum,
-      };
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       assertIsDefined(element.cursor);
       sinon
         .stub(element.cursor, 'getAddress')
@@ -888,10 +859,8 @@
     }): Promise<boolean> {
       element.loggedIn = loggedIn;
       element.path = 't.txt';
-      element.patchRange = {
-        basePatchNum: PARENT,
-        patchNum: 1 as RevisionPatchSetNum,
-      };
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       element.change = {
         ...createParsedChange(),
         _number: 42 as NumericChangeId,
@@ -990,10 +959,8 @@
     suite('url parameters', () => {
       test('_formattedFiles', () => {
         element.changeNum = 42 as NumericChangeId;
-        element.patchRange = {
-          basePatchNum: PARENT,
-          patchNum: 10 as RevisionPatchSetNum,
-        };
+        element.patchNum = 10 as RevisionPatchSetNum;
+        element.basePatchNum = PARENT;
         element.change = {
           ...createParsedChange(),
           _number: 42 as NumericChangeId,
@@ -1209,10 +1176,8 @@
 
     test('handlePatchChange calls setUrl correctly', async () => {
       element.path = 'path/to/file.txt';
-      element.patchRange = {
-        basePatchNum: PARENT,
-        patchNum: 3 as RevisionPatchSetNum,
-      };
+      element.patchNum = 3 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       await element.updateComplete;
 
       const detail = {
@@ -1365,20 +1330,18 @@
       assert.equal(saveReviewedStub.callCount, callCount);
     });
 
-    test('file review status with edit loaded', async () => {
+    test('do not set file review status for EDIT patchset', async () => {
       const saveReviewedStub = sinon.stub(
         changeModel,
         'setReviewedFilesStatus'
       );
 
-      element.patchRange = {
-        basePatchNum: 1 as BasePatchSetNum,
-        patchNum: EDIT,
-      };
+      element.patchNum = EDIT;
+      element.basePatchNum = 1 as BasePatchSetNum;
       await waitEventLoop();
 
-      assert.isTrue(element.computeEditMode());
       element.setReviewed(true);
+
       assert.isFalse(saveReviewedStub.called);
     });
 
@@ -1503,10 +1466,8 @@
         _number: 321 as NumericChangeId,
         project: 'foo/bar' as RepoName,
       };
-      element.patchRange = {
-        basePatchNum: 3 as BasePatchSetNum,
-        patchNum: 5 as RevisionPatchSetNum,
-      };
+      element.patchNum = 5 as RevisionPatchSetNum;
+      element.basePatchNum = 3 as BasePatchSetNum;
       const e = {detail: {number: 123, side: Side.RIGHT}} as CustomEvent;
 
       element.onLineSelected(e);
@@ -1527,10 +1488,8 @@
         _number: 321 as NumericChangeId,
         project: 'foo/bar' as RepoName,
       };
-      element.patchRange = {
-        basePatchNum: 3 as BasePatchSetNum,
-        patchNum: 5 as RevisionPatchSetNum,
-      };
+      element.patchNum = 5 as RevisionPatchSetNum;
+      element.basePatchNum = 3 as BasePatchSetNum;
       const e = {detail: {number: 123, side: Side.LEFT}} as CustomEvent;
 
       element.onLineSelected(e);
@@ -1611,10 +1570,8 @@
             'path/two.m4v',
             'path/three.wav',
           ]);
-          element.patchRange = {
-            patchNum: 2 as RevisionPatchSetNum,
-            basePatchNum: 1 as BasePatchSetNum,
-          };
+          element.patchNum = 2 as RevisionPatchSetNum;
+          element.basePatchNum = 1 as BasePatchSetNum;
         });
 
         suite('moveToFileWithComment previous', () => {
@@ -1673,25 +1630,6 @@
       });
     });
 
-    test('_computeEditMode', () => {
-      const callCompute = (range: PatchRange) => {
-        element.patchRange = range;
-        return element.computeEditMode();
-      };
-      assert.isFalse(
-        callCompute({
-          basePatchNum: PARENT,
-          patchNum: 1 as RevisionPatchSetNum,
-        })
-      );
-      assert.isTrue(
-        callCompute({
-          basePatchNum: 1 as BasePatchSetNum,
-          patchNum: EDIT,
-        })
-      );
-    });
-
     test('computeFileNum', () => {
       element.path = '/foo';
       assert.equal(
@@ -1758,13 +1696,14 @@
 
       test('reviewed checkbox', async () => {
         sinon.stub(element, 'handlePatchChange');
-        element.patchRange = createPatchRange();
+        element.patchNum = 1 as RevisionPatchSetNum;
+        element.basePatchNum = PARENT;
         await element.updateComplete;
 
         let checkbox = queryAndAssert(element, '#reviewed');
         assert.isTrue(isVisible(checkbox));
 
-        element.patchRange = {...element.patchRange, patchNum: EDIT};
+        element.patchNum = EDIT;
         await element.updateComplete;
 
         checkbox = queryAndAssert(element, '#reviewed');
@@ -1922,10 +1861,8 @@
         repo: 'test-project' as RepoName,
         diffView: {path: 'file1'},
       };
-      element.patchRange = {
-        patchNum: 1 as RevisionPatchSetNum,
-        basePatchNum: PARENT,
-      };
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       element.change = {
         ...createParsedChange(),
         revisions: createRevisions(1),
@@ -1946,10 +1883,8 @@
         repo: 'test-project' as RepoName,
         diffView: {path: 'file2'},
       };
-      element.patchRange = {
-        patchNum: 1 as RevisionPatchSetNum,
-        basePatchNum: PARENT,
-      };
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
 
       // No extra call
       assert.isTrue(navToDiffStub.calledOnce);
@@ -1974,10 +1909,8 @@
       element.change = createParsedChange();
       element.change.project = 'test' as RepoName;
       element.changeNum = 12 as NumericChangeId;
-      element.patchRange = {
-        patchNum: 1 as RevisionPatchSetNum,
-        basePatchNum: PARENT,
-      };
+      element.patchNum = 1 as RevisionPatchSetNum;
+      element.basePatchNum = PARENT;
       element.path = 'index.php';
       element.diff = createDiff();
       assert.deepEqual(element.computeDownloadDropdownLinks(), downloadLinks);
@@ -2006,10 +1939,8 @@
       element.change = createParsedChange();
       element.change.project = 'test' as RepoName;
       element.changeNum = 12 as NumericChangeId;
-      element.patchRange = {
-        patchNum: 3 as RevisionPatchSetNum,
-        basePatchNum: 2 as BasePatchSetNum,
-      };
+      element.patchNum = 3 as RevisionPatchSetNum;
+      element.basePatchNum = 2 as BasePatchSetNum;
       element.path = 'index.php';
       element.diff = diff;
       assert.deepEqual(element.computeDownloadDropdownLinks(), downloadLinks);