Move creating a GrDiffProcessor to the diff model

*Replacing* the current GrDiffProcessor will happen in a follow-up.

Release-Notes: skip
Google-Bug-Id: b/280019334
Change-Id: I6863d863c397e638928e36668a02e1996febe30e
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-model/gr-diff-model.ts b/polygerrit-ui/app/embed/diff/gr-diff-model/gr-diff-model.ts
index d2e997c..252f7cb 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-model/gr-diff-model.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-model/gr-diff-model.ts
@@ -3,8 +3,8 @@
  * Copyright 2023 Google LLC
  * SPDX-License-Identifier: Apache-2.0
  */
-import {Observable} from 'rxjs';
-import {filter} from 'rxjs/operators';
+import {Observable, combineLatest, from} from 'rxjs';
+import {switchMap, withLatestFrom} from 'rxjs/operators';
 import {
   DiffInfo,
   DiffPreferencesInfo,
@@ -13,13 +13,20 @@
 } from '../../../api/diff';
 import {define} from '../../../models/dependency';
 import {Model} from '../../../models/model';
-import {isDefined} from '../../../types/types';
 import {select} from '../../../utils/observable-util';
 import {
+  FullContext,
   GrDiffCommentThread,
   KeyLocations,
+  computeContext,
   computeKeyLocations,
 } from '../gr-diff/gr-diff-utils';
+import {createDefaultDiffPrefs} from '../../../constants/constants';
+import {
+  GrDiffProcessor,
+  ProcessingOptions,
+} from '../gr-diff-processor/gr-diff-processor';
+import {GrDiffGroup} from '../gr-diff/gr-diff-group';
 
 export interface DiffState {
   diff: DiffInfo;
@@ -28,34 +35,93 @@
   diffPrefs: DiffPreferencesInfo;
   lineOfInterest?: DisplayLine;
   comments: GrDiffCommentThread[];
+  groups: GrDiffGroup[];
+  /** how much context to show for large files */
+  showFullContext: FullContext;
+  isImageDiff: boolean;
 }
 
 export const diffModelToken = define<DiffModel>('diff-model');
 
-export class DiffModel extends Model<DiffState | undefined> {
+export class DiffModel extends Model<DiffState> {
   readonly diff$: Observable<DiffInfo> = select(
-    this.state$.pipe(filter(isDefined)),
+    this.state$,
     diffState => diffState.diff
   );
 
   readonly path$: Observable<string | undefined> = select(
-    this.state$.pipe(filter(isDefined)),
+    this.state$,
     diffState => diffState.path
   );
 
   readonly renderPrefs$: Observable<RenderPreferences> = select(
-    this.state$.pipe(filter(isDefined)),
+    this.state$,
     diffState => diffState.renderPrefs
   );
 
   readonly diffPrefs$: Observable<DiffPreferencesInfo> = select(
-    this.state$.pipe(filter(isDefined)),
+    this.state$,
     diffState => diffState.diffPrefs
   );
 
+  readonly context$: Observable<number> = select(this.state$, state =>
+    computeContext(
+      state.diffPrefs.context,
+      state.showFullContext,
+      createDefaultDiffPrefs().context
+    )
+  );
+
+  readonly isImageDiff$: Observable<boolean> = select(
+    this.state$,
+    diffState => diffState.isImageDiff
+  );
+
   readonly keyLocations$: Observable<KeyLocations> = select(
-    this.state$.pipe(filter(isDefined)),
+    this.state$,
     diffState =>
       computeKeyLocations(diffState.lineOfInterest, diffState.comments ?? [])
   );
+
+  constructor() {
+    super({
+      diff: {content: [], change_type: 'MODIFIED', intraline_status: 'OK'},
+      diffPrefs: createDefaultDiffPrefs(),
+      renderPrefs: {},
+      comments: [],
+      groups: [],
+      showFullContext: FullContext.UNDECIDED,
+      isImageDiff: false,
+    });
+    this.subscriptions = [this.processDiff()];
+  }
+
+  processDiff() {
+    return combineLatest([
+      this.diff$,
+      this.context$,
+      this.renderPrefs$,
+      this.isImageDiff$,
+    ])
+      .pipe(
+        withLatestFrom(this.keyLocations$),
+        switchMap(
+          ([[diff, context, renderPrefs, isImageDiff], keyLocations]) => {
+            const options: ProcessingOptions = {
+              context,
+              keyLocations,
+              isBinary: !!(isImageDiff || diff.binary),
+            };
+            const processor = new GrDiffProcessor(undefined, options);
+            if (renderPrefs?.num_lines_rendered_at_once) {
+              options.asyncThreshold = renderPrefs.num_lines_rendered_at_once;
+            }
+            return from(processor.process(diff.content));
+          }
+        )
+      )
+      .subscribe(groups => {
+        this.updateState({groups});
+      });
+  }
 }
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor.ts b/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor.ts
index 483a4da..70fe338 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor.ts
@@ -15,9 +15,7 @@
 import {assert} from '../../../utils/common-util';
 import {GrAnnotation} from '../gr-diff-highlight/gr-annotation';
 import {FILE, GrDiffLineType, LineNumber} from '../../../api/diff';
-import {KeyLocations} from '../gr-diff/gr-diff-utils';
-
-const WHOLE_FILE = -1;
+import {FULL_CONTEXT, KeyLocations} from '../gr-diff/gr-diff-utils';
 
 // visible for testing
 export interface State {
@@ -134,58 +132,23 @@
    * @return A promise that resolves with an
    * array of GrDiffGroups when the diff is completely processed.
    */
-  process(chunks: DiffContent[]) {
+  async process(chunks: DiffContent[]): Promise<GrDiffGroup[]> {
     assert(this.isStarted === false, 'diff processor cannot be started twice');
 
     window.addEventListener('scroll', this.handleWindowScroll);
 
     this.consumer?.clearGroups();
-    this.consumer?.addGroup(this.makeGroup('LOST'));
-    this.consumer?.addGroup(this.makeGroup(FILE));
+    const groups = [this.makeGroup('LOST'), this.makeGroup(FILE)];
+    this.consumer?.addGroup(groups[0]);
+    this.consumer?.addGroup(groups[1]);
 
-    if (this.isBinary) return Promise.resolve();
-
-    return new Promise<void>(resolve => {
-      const state = {
-        lineNums: {left: 0, right: 0},
-        chunkIndex: 0,
-      };
-
-      chunks = this.splitLargeChunks(chunks);
-      chunks = this.splitCommonChunksWithKeyLocations(chunks);
-
-      let currentBatch = 0;
-      const nextStep = () => {
-        if (this.isCancelled || state.chunkIndex >= chunks.length) {
-          resolve();
-          return;
-        }
-        if (this.isScrolling) {
-          window.setTimeout(nextStep, 100);
-          return;
-        }
-
-        const stateUpdate = this.processNext(state, chunks);
-        for (const group of stateUpdate.groups) {
-          this.consumer?.addGroup(group);
-          currentBatch += group.lines.length;
-        }
-        state.lineNums.left += stateUpdate.lineDelta.left;
-        state.lineNums.right += stateUpdate.lineDelta.right;
-
-        state.chunkIndex = stateUpdate.newChunkIndex;
-        if (currentBatch >= this.asyncThreshold) {
-          currentBatch = 0;
-          window.setTimeout(nextStep, 1);
-        } else {
-          nextStep.call(this);
-        }
-      };
-
-      nextStep.call(this);
-    }).finally(() => {
+    if (this.isBinary) return groups;
+    try {
+      await this.processChunks(chunks);
+    } finally {
       this.finish();
-    });
+    }
+    return groups;
   }
 
   finish() {
@@ -198,6 +161,50 @@
     this.finish();
   }
 
+  async processChunks(chunks: DiffContent[]) {
+    let completed = () => {};
+    const promise = new Promise<void>(resolve => (completed = resolve));
+
+    const state = {
+      lineNums: {left: 0, right: 0},
+      chunkIndex: 0,
+    };
+
+    chunks = this.splitLargeChunks(chunks);
+    chunks = this.splitCommonChunksWithKeyLocations(chunks);
+
+    let currentBatch = 0;
+    const nextStep = () => {
+      if (this.isCancelled || state.chunkIndex >= chunks.length) {
+        completed();
+        return;
+      }
+      if (this.isScrolling) {
+        window.setTimeout(nextStep, 100);
+        return;
+      }
+
+      const stateUpdate = this.processNext(state, chunks);
+      for (const group of stateUpdate.groups) {
+        this.consumer?.addGroup(group);
+        currentBatch += group.lines.length;
+      }
+      state.lineNums.left += stateUpdate.lineDelta.left;
+      state.lineNums.right += stateUpdate.lineDelta.right;
+
+      state.chunkIndex = stateUpdate.newChunkIndex;
+      if (currentBatch >= this.asyncThreshold) {
+        currentBatch = 0;
+        window.setTimeout(nextStep, 1);
+      } else {
+        nextStep.call(this);
+      }
+    };
+
+    nextStep.call(this);
+    await promise;
+  }
+
   /**
    * Process the next uncollapsible chunk, or the next collapsible chunks.
    */
@@ -286,7 +293,7 @@
     );
 
     const hasSkippedGroup = !!groups.find(g => g.skip);
-    if (this.context !== WHOLE_FILE || hasSkippedGroup) {
+    if (this.context !== FULL_CONTEXT || hasSkippedGroup) {
       const contextNumLines = this.context > 0 ? this.context : 0;
       const hiddenStart = state.chunkIndex === 0 ? 0 : contextNumLines;
       const hiddenEnd =
@@ -477,7 +484,10 @@
       // enabled for any other context preference because manipulating the
       // chunks in this way violates assumptions by the context grouper logic.
       const MAX_GROUP_SIZE = calcMaxGroupSize(this.asyncThreshold);
-      if (this.context === -1 && chunk.ab.length > MAX_GROUP_SIZE * 2) {
+      if (
+        this.context === FULL_CONTEXT &&
+        chunk.ab.length > MAX_GROUP_SIZE * 2
+      ) {
         // Split large shared chunks in two, where the first is the maximum
         // group size.
         newChunks.push({ab: chunk.ab.slice(0, MAX_GROUP_SIZE)});
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor_test.ts b/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor_test.ts
index 706c208..49dab4f 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-processor/gr-diff-processor_test.ts
@@ -16,9 +16,9 @@
 import {DiffContent} from '../../../types/diff';
 import {assert} from '@open-wc/testing';
 import {FILE, GrDiffLineType} from '../../../api/diff';
+import {FULL_CONTEXT} from '../gr-diff/gr-diff-utils';
 
 suite('gr-diff-processor tests', () => {
-  const WHOLE_FILE = -1;
   const loremIpsum =
     'Lorem ipsum dolor sit amet, ei nonumes vituperata ius. ' +
     'Duo  animal omnesque fabellas et. Id has phaedrum dignissim ' +
@@ -620,7 +620,7 @@
         .fill(0)
         .map(() => `${Math.random()}`);
       const content = [{ab}];
-      processor.context = -1;
+      processor.context = FULL_CONTEXT;
       const result = processor.splitLargeChunks(content);
       assert.equal(result.length, 2);
       assert.deepEqual(result[0].ab, content[0].ab.slice(0, maxGroupSize));
@@ -836,8 +836,8 @@
         rows = loremIpsum.split(' ');
       });
 
-      test('WHOLE_FILE', () => {
-        processor.context = WHOLE_FILE;
+      test('FULL_CONTEXT', () => {
+        processor.context = FULL_CONTEXT;
         const state: State = {
           lineNums: {left: 10, right: 100},
           chunkIndex: 1,
@@ -870,8 +870,8 @@
         );
       });
 
-      test('WHOLE_FILE with skip chunks still get collapsed', () => {
-        processor.context = WHOLE_FILE;
+      test('FULL_CONTEXT with skip chunks still get collapsed', () => {
+        processor.context = FULL_CONTEXT;
         const lineNums = {left: 10, right: 100};
         const state = {
           lineNums,
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils.ts
index d309556..0b688a6 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils.ts
@@ -188,6 +188,45 @@
   right: {[key: string]: boolean};
 }
 
+/**
+ * "Context" is the number of lines that we are showing around diff chunks and
+ * commented lines. This typically comes from a user preference and is set to
+ * something like 3 or 10.
+ *
+ * `FULL_CONTEXT` means that the user wants to see the entire file. We could
+ * also call this "infinite context".
+ */
+export const FULL_CONTEXT = -1;
+
+export enum FullContext {
+  /** User has opted into showing the full context. */
+  YES = 'YES',
+  /** User has opted into showing only limited context. */
+  NO = 'NO',
+  /**
+   * User has not decided yet. Will see a warning message with two options then,
+   * if the file is too large.
+   */
+  UNDECIDED = 'UNDECIDED',
+}
+
+export function computeContext(
+  prefsContext: number | undefined,
+  showFullContext: FullContext,
+  defaultContext: number
+) {
+  if (showFullContext === FullContext.YES) {
+    return FULL_CONTEXT;
+  }
+  if (
+    prefsContext &&
+    !(showFullContext === FullContext.NO && prefsContext === FULL_CONTEXT)
+  ) {
+    return prefsContext;
+  }
+  return defaultContext;
+}
+
 export function computeKeyLocations(
   lineOfInterest: DisplayLine | undefined,
   comments: GrDiffCommentThread[]
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils_test.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils_test.ts
index 7e6e7fc..28c6c08 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff-utils_test.ts
@@ -15,6 +15,9 @@
   toCommentThreadModel,
   compareComments,
   GrDiffThreadElement,
+  computeContext,
+  FULL_CONTEXT,
+  FullContext,
 } from './gr-diff-utils';
 import {FILE, LOST, Side} from '../../../api/diff';
 
@@ -184,6 +187,26 @@
     assert.isUndefined(getRange(threadEl));
   });
 
+  suite('computeContext', () => {
+    test('computeContext 1', () => {
+      assert.equal(computeContext(1, FullContext.YES, 2), FULL_CONTEXT);
+      assert.equal(computeContext(1, FullContext.NO, 2), 1);
+      assert.equal(computeContext(1, FullContext.UNDECIDED, 2), 1);
+    });
+
+    test('computeContext FULL_CONTEXT', () => {
+      assert.equal(
+        computeContext(FULL_CONTEXT, FullContext.YES, 2),
+        FULL_CONTEXT
+      );
+      assert.equal(computeContext(FULL_CONTEXT, FullContext.NO, 2), 2);
+      assert.equal(
+        computeContext(FULL_CONTEXT, FullContext.UNDECIDED, 2),
+        FULL_CONTEXT
+      );
+    });
+  });
+
   suite('key locations', () => {
     test('lineOfInterest is a key location', () => {
       const lineOfInterest = {lineNum: 789, side: Side.LEFT};
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
index 1f212b0..54d578a 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
@@ -28,6 +28,7 @@
   compareComments,
   toCommentThreadModel,
   KeyLocations,
+  FullContext,
 } from '../gr-diff/gr-diff-utils';
 import {BlameInfo, CommentRange, ImageInfo} from '../../../types/common';
 import {DiffInfo, DiffPreferencesInfo} from '../../../types/diff';
@@ -40,11 +41,7 @@
   CommentRangeLayer,
   GrRangedCommentLayer,
 } from '../gr-ranged-comment-layer/gr-ranged-comment-layer';
-import {
-  createDefaultDiffPrefs,
-  DiffViewMode,
-  Side,
-} from '../../../constants/constants';
+import {DiffViewMode, Side} from '../../../constants/constants';
 import {
   GrDiffProcessor,
   ProcessingOptions,
@@ -236,17 +233,6 @@
   @property({type: Boolean})
   override isContentEditable = isSafari();
 
-  /**
-   * Whether the safety check for large diffs when whole-file is set has
-   * been bypassed. If the value is null, then the safety has not been
-   * bypassed. If the value is a number, then that number represents the
-   * context preference to use when rendering the bypassed diff.
-   *
-   * Private but used in tests.
-   */
-  @state()
-  safetyBypass: number | null = null;
-
   // Private but used in tests.
   @state()
   showWarning?: boolean;
@@ -288,7 +274,7 @@
   // Private but used in tests.
   highlights = new GrDiffHighlight();
 
-  private diffModel = new DiffModel(undefined);
+  private diffModel = new DiffModel();
 
   // visible for testing
   builder?: GrDiffBuilder;
@@ -312,6 +298,7 @@
 
   private rangeLayer?: GrRangedCommentLayer;
 
+  // TODO: Remove. Let the model instantiate the processor.
   // visible for testing
   processor?: GrDiffProcessor;
 
@@ -322,8 +309,12 @@
    */
   private groups: GrDiffGroup[] = [];
 
+  // TODO: Can be removed when GrDiffProcessor is not instantiated anymore.
   private keyLocations: KeyLocations = {left: {}, right: {}};
 
+  // TODO: Can be removed when GrDiffProcessor is not instantiated anymore.
+  private context = 3;
+
   static override get styles() {
     return [
       iconStyles,
@@ -342,6 +333,11 @@
       () => this.diffModel.keyLocations$,
       keyLocations => (this.keyLocations = keyLocations)
     );
+    subscribe(
+      this,
+      () => this.diffModel.context$,
+      context => (this.context = context)
+    );
     this.addEventListener(
       'create-range-comment',
       (e: CustomEvent<CreateRangeCommentEventDetail>) =>
@@ -384,13 +380,16 @@
       changedProperties.has('prefs') ||
       changedProperties.has('lineOfInterest')
     ) {
-      this.diffModel.updateState({
-        diff: this.diff,
-        path: this.path,
-        renderPrefs: this.renderPrefs,
-        diffPrefs: this.prefs,
-        lineOfInterest: this.lineOfInterest,
-      });
+      if (this.diff && this.prefs) {
+        this.diffModel.updateState({
+          diff: this.diff,
+          path: this.path,
+          renderPrefs: this.renderPrefs ?? {},
+          diffPrefs: this.prefs,
+          lineOfInterest: this.lineOfInterest,
+          isImageDiff: this.isImageDiff,
+        });
+      }
     }
     if (
       changedProperties.has('path') ||
@@ -763,7 +762,7 @@
   private cleanup() {
     this.cancel();
     this.blame = null;
-    this.safetyBypass = null;
+    this.diffModel.updateState({showFullContext: FullContext.UNDECIDED});
     this.showWarning = false;
     this.clearDiffContent();
   }
@@ -911,10 +910,10 @@
       return;
     }
     if (
-      this.getBypassPrefs().context === -1 &&
+      this.prefs.context === FULL_CONTEXT &&
+      this.diffModel.getState().showFullContext === FullContext.UNDECIDED &&
       this.diffLength &&
-      this.diffLength >= LARGE_DIFF_THRESHOLD_LINES &&
-      this.safetyBypass === null
+      this.diffLength >= LARGE_DIFF_THRESHOLD_LINES
     ) {
       this.showWarning = true;
       fire(this, 'render', {});
@@ -1048,18 +1047,6 @@
     lostCell.insertBefore(div, lostCell.firstChild);
   }
 
-  /**
-   * Get the preferences object including the safety bypass context (if any).
-   */
-  // visible for testing
-  getBypassPrefs() {
-    assertIsDefined(this.prefs, 'prefs');
-    if (this.safetyBypass !== null) {
-      return {...this.prefs, context: this.safetyBypass};
-    }
-    return this.prefs;
-  }
-
   clearDiffContent() {
     this.unobserveNodes();
     if (!this.diffTable) return;
@@ -1083,28 +1070,23 @@
   }
 
   private handleFullBypass() {
-    this.safetyBypass = FULL_CONTEXT;
+    this.diffModel.updateState({showFullContext: FullContext.YES});
     this.debounceRenderDiffTable();
   }
 
   private collapseContext() {
+    this.diffModel.updateState({showFullContext: FullContext.NO});
     // Uses the default context amount if the preference is for the entire file.
-    this.safetyBypass =
-      this.prefs?.context && this.prefs.context >= 0
-        ? null
-        : createDefaultDiffPrefs().context;
     this.debounceRenderDiffTable();
   }
 
+  // TODO: Migrate callers to just update prefs.context.
   toggleAllContext() {
-    if (!this.prefs) {
-      return;
-    }
-    if (this.getBypassPrefs().context < 0) {
-      this.collapseContext();
-    } else {
-      this.handleFullBypass();
-    }
+    const current = this.diffModel.getState().showFullContext;
+    this.diffModel.updateState({
+      showFullContext:
+        current === FullContext.YES ? FullContext.NO : FullContext.YES,
+    });
   }
 
   private computeNewlineWarning(): string | undefined {
@@ -1152,7 +1134,7 @@
     this.builder.addColumns(this.diffTable, getLineNumberCellWidth(this.prefs));
 
     const options: ProcessingOptions = {
-      context: this.getBypassPrefs().context,
+      context: this.context,
       keyLocations: this.keyLocations,
       isBinary: !!(this.isImageDiff || this.diff.binary),
     };
diff --git a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts
index 99db4e9..5603edb 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff_test.ts
@@ -3801,89 +3801,6 @@
     });
   });
 
-  suite('safety and bypass', () => {
-    let renderStub: sinon.SinonStub;
-
-    setup(async () => {
-      renderStub = sinon.stub(element, 'legacyRender').callsFake(() => {
-        assertIsDefined(element.diffTable);
-        element.diffTable.dispatchEvent(
-          new CustomEvent('render', {bubbles: true, composed: true})
-        );
-        return Promise.resolve();
-      });
-      sinon.stub(element, 'getDiffLength').returns(10000);
-      element.diff = createDiff();
-      element.noRenderOnPrefsChange = true;
-      await element.updateComplete;
-    });
-
-    test('large render w/ context = 10', async () => {
-      element.prefs = {...MINIMAL_PREFS, context: 10};
-      element.renderDiffTable();
-      await waitForEventOnce(element, 'render');
-
-      assert.isTrue(renderStub.called);
-      assert.isFalse(element.showWarning);
-    });
-
-    test('large render w/ whole file and bypass', async () => {
-      element.prefs = {...MINIMAL_PREFS, context: -1};
-      element.safetyBypass = 10;
-      element.renderDiffTable();
-      await waitForEventOnce(element, 'render');
-
-      assert.isTrue(renderStub.called);
-      assert.isFalse(element.showWarning);
-    });
-
-    test('large render w/ whole file and no bypass', async () => {
-      element.prefs = {...MINIMAL_PREFS, context: -1};
-      element.renderDiffTable();
-      await waitForEventOnce(element, 'render');
-
-      assert.isFalse(renderStub.called);
-      assert.isTrue(element.showWarning);
-    });
-
-    test('toggles expand context using bypass', async () => {
-      element.prefs = {...MINIMAL_PREFS, context: 3};
-
-      element.toggleAllContext();
-      element.renderDiffTable();
-      await element.updateComplete;
-
-      assert.equal(element.prefs.context, 3);
-      assert.equal(element.safetyBypass, -1);
-      assert.equal(element.getBypassPrefs().context, -1);
-    });
-
-    test('toggles collapse context from bypass', async () => {
-      element.prefs = {...MINIMAL_PREFS, context: 3};
-      element.safetyBypass = -1;
-
-      element.toggleAllContext();
-      element.renderDiffTable();
-      await element.updateComplete;
-
-      assert.equal(element.prefs.context, 3);
-      assert.isNull(element.safetyBypass);
-      assert.equal(element.getBypassPrefs().context, 3);
-    });
-
-    test('toggles collapse context from pref using default', async () => {
-      element.prefs = {...MINIMAL_PREFS, context: -1};
-
-      element.toggleAllContext();
-      element.renderDiffTable();
-      await element.updateComplete;
-
-      assert.equal(element.prefs.context, -1);
-      assert.equal(element.safetyBypass, 10);
-      assert.equal(element.getBypassPrefs().context, 10);
-    });
-  });
-
   suite('blame', () => {
     test('unsetting', async () => {
       element.blame = [];