Merge "Move styles from dropdown-content to GrAutocompleteDropdown"
diff --git a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
index 23de6db..396ba74 100644
--- a/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
+++ b/java/com/google/gerrit/httpd/restapi/RestApiServlet.java
@@ -30,7 +30,6 @@
 import static com.google.common.net.HttpHeaders.CONTENT_TYPE;
 import static com.google.common.net.HttpHeaders.ORIGIN;
 import static com.google.common.net.HttpHeaders.VARY;
-import static com.google.gerrit.server.experiments.ExperimentFeaturesConstants.GERRIT_BACKEND_REQUEST_FEATURE_REMOVE_REVISION_ETAG;
 import static java.math.RoundingMode.CEILING;
 import static java.nio.charset.StandardCharsets.ISO_8859_1;
 import static java.nio.charset.StandardCharsets.UTF_8;
@@ -120,9 +119,7 @@
 import com.google.gerrit.server.cancellation.RequestStateContext;
 import com.google.gerrit.server.cancellation.RequestStateProvider;
 import com.google.gerrit.server.change.ChangeFinder;
-import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.config.GerritServerConfig;
-import com.google.gerrit.server.experiments.ExperimentFeatures;
 import com.google.gerrit.server.group.GroupAuditService;
 import com.google.gerrit.server.logging.Metadata;
 import com.google.gerrit.server.logging.PerformanceLogContext;
@@ -270,7 +267,6 @@
     final PluginSetContext<ExceptionHook> exceptionHooks;
     final Injector injector;
     final DynamicMap<DynamicOptions.DynamicBean> dynamicBeans;
-    final ExperimentFeatures experimentFeatures;
     final DeadlineChecker.Factory deadlineCheckerFactory;
     final CancellationMetrics cancellationMetrics;
 
@@ -291,7 +287,6 @@
         PluginSetContext<ExceptionHook> exceptionHooks,
         Injector injector,
         DynamicMap<DynamicOptions.DynamicBean> dynamicBeans,
-        ExperimentFeatures experimentFeatures,
         DeadlineChecker.Factory deadlineCheckerFactory,
         CancellationMetrics cancellationMetrics) {
       this.currentUser = currentUser;
@@ -310,7 +305,6 @@
       allowOrigin = makeAllowOrigin(config);
       this.injector = injector;
       this.dynamicBeans = dynamicBeans;
-      this.experimentFeatures = experimentFeatures;
       this.deadlineCheckerFactory = deadlineCheckerFactory;
       this.cancellationMetrics = cancellationMetrics;
     }
@@ -862,11 +856,6 @@
         TraceContext.newTimer(
             "RestApiServlet#getEtagWithRetry:resource",
             Metadata.builder().restViewName(rsrc.getClass().getSimpleName()).build())) {
-      if (rsrc instanceof RevisionResource
-          && globals.experimentFeatures.isFeatureEnabled(
-              GERRIT_BACKEND_REQUEST_FEATURE_REMOVE_REVISION_ETAG)) {
-        return null;
-      }
       return invokeRestEndpointWithRetry(
           req,
           traceContext,
diff --git a/java/com/google/gerrit/index/testing/BUILD b/java/com/google/gerrit/index/testing/BUILD
index 9af2598..44bf70d 100644
--- a/java/com/google/gerrit/index/testing/BUILD
+++ b/java/com/google/gerrit/index/testing/BUILD
@@ -10,13 +10,9 @@
     deps = [
         "//java/com/google/gerrit/common:annotations",
         "//java/com/google/gerrit/entities",
-        "//java/com/google/gerrit/exceptions",
         "//java/com/google/gerrit/index",
-        "//java/com/google/gerrit/index:query_exception",
         "//java/com/google/gerrit/index/project",
-        "//java/com/google/gerrit/proto",
         "//java/com/google/gerrit/server",
-        "//java/com/google/gerrit/server/logging",
         "//lib:guava",
         "//lib:jgit",
         "//lib:protobuf",
diff --git a/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java b/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java
index efc8d54..5a4580c 100644
--- a/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java
+++ b/java/com/google/gerrit/server/experiments/ExperimentFeaturesConstants.java
@@ -24,9 +24,6 @@
 
   public static String UI_FEATURE_SUBMIT_REQUIREMENTS_UI = "UiFeature__submit_requirements_ui";
 
-  public static String GERRIT_BACKEND_REQUEST_FEATURE_REMOVE_REVISION_ETAG =
-      "GerritBackendRequestFeature__remove_revision_etag";
-
   public static String GERRIT_BACKEND_FEATURE_ATTACH_NONCE_TO_DOCUMENTATION =
       "GerritBackendFeature__attach_nonce_to_documentation";
 
diff --git a/javatests/com/google/gerrit/json/BUILD b/javatests/com/google/gerrit/json/BUILD
index 575f575..a242b0e 100644
--- a/javatests/com/google/gerrit/json/BUILD
+++ b/javatests/com/google/gerrit/json/BUILD
@@ -5,7 +5,6 @@
     srcs = glob(["*.java"]),
     deps = [
         "//java/com/google/gerrit/json",
-        "//java/com/google/gerrit/server/util/time",
         "//java/com/google/gerrit/testing:gerrit-test-util",
         "//lib:gson",
         "//lib:guava",
diff --git a/javatests/com/google/gerrit/server/cache/BUILD b/javatests/com/google/gerrit/server/cache/BUILD
index c708e09..275f2ec 100644
--- a/javatests/com/google/gerrit/server/cache/BUILD
+++ b/javatests/com/google/gerrit/server/cache/BUILD
@@ -7,11 +7,9 @@
     deps = [
         "//java/com/google/gerrit/server",
         "//java/com/google/gerrit/testing:gerrit-test-util",
-        "//javatests/com/google/gerrit/util/http/testutil",
         "//lib:junit",
         "//lib/truth",
         "//lib/truth:truth-java8-extension",
-        "@servlet-api//jar",
     ],
 )
 
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts
index 18c1455..6a74dce 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.ts
@@ -462,7 +462,14 @@
       this.computeShowRepoBranchTogether(),
       () =>
         html`<section class=${this.computeDisplayState(Metadata.REPO_BRANCH)}>
-          <span class="title">Repo | Branch</span>
+          <span class="title">
+            <gr-tooltip-content
+              has-tooltip
+              title="Repository and branch that the change will be merged into if submitted."
+            >
+              Repo | Branch
+            </gr-tooltip-content>
+          </span>
           <span class="value">
             <a href=${this.computeProjectUrl(change.project)}
               >${change.project}</a
@@ -474,10 +481,17 @@
           </span>
         </section>`,
 
-      () => html` <section
+      () => html`<section
           class=${this.computeDisplayState(Metadata.REPO_BRANCH)}
         >
-          <span class="title">Repo</span>
+          <span class="title">
+            <gr-tooltip-content
+              has-tooltip
+              title="Repository that the change will be merged into if submitted."
+            >
+              Repo
+            </gr-tooltip-content>
+          </span>
           <span class="value">
             <a href=${this.computeProjectUrl(change.project)}>
               <gr-limited-text
@@ -488,7 +502,14 @@
           </span>
         </section>
         <section class=${this.computeDisplayState(Metadata.REPO_BRANCH)}>
-          <span class="title">Branch</span>
+          <span class="title">
+            <gr-tooltip-content
+              has-tooltip
+              title="Branch that the change will be merged into if submitted."
+            >
+              Branch
+            </gr-tooltip-content>
+          </span>
           <span class="value">
             <a href=${this.computeBranchUrl(change.project, change.branch)}>
               <gr-limited-text
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.ts b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.ts
index 50bb9d9..e45de51 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.ts
@@ -165,7 +165,12 @@
       </section>
       <section>
           <span class="title">
-            Repo | Branch
+            <gr-tooltip-content
+              has-tooltip=""
+              title="Repository and branch that the change will be merged into if submitted."
+            >
+              Repo | Branch
+            </gr-tooltip-content>
           </span>
           <span class="value">
             <a href="/q/project:test-project">
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element_test.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element_test.ts
index 210b8b2..5eabc59 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element_test.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-element_test.ts
@@ -71,7 +71,7 @@
     const text = 'a'.repeat(51);
     const expected = 'a'.repeat(50) + WBR_HTML + 'a';
     const result = builder.createTextEl(null, line(text)).firstElementChild
-      ?.innerHTML;
+      ?.firstElementChild?.innerHTML;
     assert.equal(result, expected);
   });
 
@@ -80,7 +80,7 @@
     const text = 'a'.repeat(51);
     const expected = 'a'.repeat(50) + LINE_BREAK_HTML + 'a';
     const result = builder.createTextEl(null, line(text)).firstElementChild
-      ?.innerHTML;
+      ?.firstElementChild?.innerHTML;
     assert.equal(result, expected);
   });
 
@@ -119,7 +119,7 @@
     assert.equal(el.innerText, text);
     // With line length 10 and tab size 4, there should be a line break
     // after every two tabs.
-    const newlineEl = el.querySelector('.contentText > .br');
+    const newlineEl = el.querySelector('.contentText .br');
     assert.isOk(newlineEl);
     assert.equal(
       el.querySelector('.contentText .tab:nth-child(2)')?.nextSibling,
diff --git a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-legacy.ts b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-legacy.ts
index 8176e14..2c9f210 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-legacy.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff-builder/gr-diff-builder-legacy.ts
@@ -396,6 +396,17 @@
     } else if (line.beforeNumber === 'FILE') td.classList.add('file');
     else if (line.beforeNumber === 'LOST') td.classList.add('lost');
 
+    if (side && line.lineNumber(side)) {
+      const lineNumber = line.lineNumber(side);
+      const threadGroupEl = document.createElement('div');
+      threadGroupEl.className = 'thread-group';
+      threadGroupEl.setAttribute('data-side', side);
+      const slot = document.createElement('slot');
+      slot.name = `${side}-${lineNumber}`;
+      threadGroupEl.appendChild(slot);
+      td.appendChild(threadGroupEl);
+    }
+
     return td;
   }
 
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 8a17611..f583c2e 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
@@ -274,6 +274,12 @@
   elementId: string
 ): HTMLElement {
   const contentText = createElementDiff('div', 'contentText');
+  // <gr-legacy-text> is not defined anywhere, so this behave just as a <div>
+  // would. We use this during the migration to lit based diff elements to
+  // match <gr-diff-text>. We define a css rule with `display:contents` making
+  // sure that this extra element is basically a no-op.
+  const legacyText = document.createElement('gr-legacy-text');
+  contentText.appendChild(legacyText);
   contentText.id = elementId;
   let columnPos = 0;
   let textOffset = 0;
@@ -285,16 +291,16 @@
       let rowStart = 0;
       let rowEnd = lineLimit - columnPos;
       while (rowEnd < segment.length) {
-        contentText.appendChild(
+        legacyText.appendChild(
           document.createTextNode(segment.substring(rowStart, rowEnd))
         );
-        contentText.appendChild(createLineBreak(responsiveMode));
+        legacyText.appendChild(createLineBreak(responsiveMode));
         columnPos = 0;
         rowStart = rowEnd;
         rowEnd += lineLimit;
       }
       // Append the last part of |segment|, which fits on the current line.
-      contentText.appendChild(
+      legacyText.appendChild(
         document.createTextNode(segment.substring(rowStart))
       );
       columnPos += segment.length - rowStart;
@@ -306,20 +312,20 @@
         // Append a single '\t' character.
         let effectiveTabSize = tabSize - (columnPos % tabSize);
         if (columnPos + effectiveTabSize > lineLimit) {
-          contentText.appendChild(createLineBreak(responsiveMode));
+          legacyText.appendChild(createLineBreak(responsiveMode));
           columnPos = 0;
           effectiveTabSize = tabSize;
         }
-        contentText.appendChild(createTabWrapper(effectiveTabSize));
+        legacyText.appendChild(createTabWrapper(effectiveTabSize));
         columnPos += effectiveTabSize;
         textOffset++;
       } else {
         // Append a single surrogate pair.
         if (columnPos >= lineLimit) {
-          contentText.appendChild(createLineBreak(responsiveMode));
+          legacyText.appendChild(createLineBreak(responsiveMode));
           columnPos = 0;
         }
-        contentText.appendChild(
+        legacyText.appendChild(
           document.createTextNode(text.substring(textOffset, textOffset + 2))
         );
         textOffset += 2;
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 7e8eb4c..98b4586 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
@@ -20,10 +20,13 @@
   test('formatText newlines 1', () => {
     let text = 'abcdef';
 
-    assert.equal(formatText(text, 'NONE', 4, 10, '').innerHTML, text);
+    assert.equal(
+      formatText(text, 'NONE', 4, 10, '').firstElementChild?.innerHTML,
+      text
+    );
     text = 'a'.repeat(20);
     assert.equal(
-      formatText(text, 'NONE', 4, 10, '').innerHTML,
+      formatText(text, 'NONE', 4, 10, '').firstElementChild?.innerHTML,
       'a'.repeat(10) + LINE_BREAK_HTML + 'a'.repeat(10)
     );
   });
@@ -31,7 +34,7 @@
   test('formatText newlines 2', () => {
     const text = '<span class="thumbsup">👍</span>';
     assert.equal(
-      formatText(text, 'NONE', 4, 10, '').innerHTML,
+      formatText(text, 'NONE', 4, 10, '').firstElementChild?.innerHTML,
       '&lt;span clas' +
         LINE_BREAK_HTML +
         's="thumbsu' +
@@ -45,7 +48,7 @@
   test('formatText newlines 3', () => {
     const text = '01234\t56789';
     assert.equal(
-      formatText(text, 'NONE', 4, 10, '').innerHTML,
+      formatText(text, 'NONE', 4, 10, '').firstElementChild?.innerHTML,
       '01234' + createTabWrapper(3).outerHTML + '56' + LINE_BREAK_HTML + '789'
     );
   });
@@ -53,7 +56,7 @@
   test('formatText newlines 4', () => {
     const text = '👍'.repeat(58);
     assert.equal(
-      formatText(text, 'NONE', 4, 20, '').innerHTML,
+      formatText(text, 'NONE', 4, 20, '').firstElementChild?.innerHTML,
       '👍'.repeat(20) +
         LINE_BREAK_HTML +
         '👍'.repeat(20) +
@@ -82,7 +85,8 @@
     assert.ok(wrapper);
     assert.equal(wrapper.innerText, '\t');
     assert.equal(
-      formatText(html, 'NONE', tabSize, Infinity, '').innerHTML,
+      formatText(html, 'NONE', tabSize, Infinity, '').firstElementChild
+        ?.innerHTML,
       'abc' + wrapper.outerHTML + 'def'
     );
   });
@@ -91,31 +95,22 @@
     let input = '<script>alert("XSS");<' + '/script>';
     let expected = '&lt;script&gt;alert("XSS");&lt;/script&gt;';
 
-    let result = formatText(
-      input,
-      'NONE',
-      1,
-      Number.POSITIVE_INFINITY,
-      ''
-    ).innerHTML;
+    let result = formatText(input, 'NONE', 1, Number.POSITIVE_INFINITY, '')
+      .firstElementChild?.innerHTML;
     assert.equal(result, expected);
 
     input = '& < > " \' / `';
     expected = '&amp; &lt; &gt; " \' / `';
-    result = formatText(
-      input,
-      'NONE',
-      1,
-      Number.POSITIVE_INFINITY,
-      ''
-    ).innerHTML;
+    result = formatText(input, 'NONE', 1, Number.POSITIVE_INFINITY, '')
+      .firstElementChild?.innerHTML;
     assert.equal(result, expected);
   });
 
   test('text length with tabs and unicode', () => {
     function expectTextLength(text: string, tabSize: number, expected: number) {
       // Formatting to |expected| columns should not introduce line breaks.
-      const result = formatText(text, 'NONE', tabSize, expected, '');
+      const result = formatText(text, 'NONE', tabSize, expected, '')
+        .firstElementChild!;
       assert.isNotOk(
         result.querySelector('.contentText > .br'),
         '  Expected the result of: \n' +
@@ -126,19 +121,22 @@
 
       // Increasing the line limit should produce the same markup.
       assert.equal(
-        formatText(text, 'NONE', tabSize, Infinity, '').innerHTML,
+        formatText(text, 'NONE', tabSize, Infinity, '').firstElementChild
+          ?.innerHTML,
         result.innerHTML
       );
       assert.equal(
-        formatText(text, 'NONE', tabSize, expected + 1, '').innerHTML,
+        formatText(text, 'NONE', tabSize, expected + 1, '').firstElementChild
+          ?.innerHTML,
         result.innerHTML
       );
 
       // Decreasing the line limit should introduce line breaks.
       if (expected > 0) {
-        const tooSmall = formatText(text, 'NONE', tabSize, expected - 1, '');
+        const tooSmall = formatText(text, 'NONE', tabSize, expected - 1, '')
+          .firstElementChild!;
         assert.isOk(
-          tooSmall.querySelector('.contentText > .br'),
+          tooSmall.querySelector('.contentText .br'),
           '  Expected the result of: \n' +
             `      _formatText(${text}', ${tabSize}, ${expected - 1})\n` +
             '  to contain a br. But the actual result HTML was:\n' +
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 6029e65..f9a31b4 100644
--- a/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
+++ b/polygerrit-ui/app/embed/diff/gr-diff/gr-diff.ts
@@ -665,6 +665,8 @@
           background-color: var(--view-background-color);
         }
 
+        .content .contentText gr-diff-text:empty:after,
+        .content .contentText gr-legacy-text:empty:after,
         .content .contentText:empty:after {
           /* Newline, to ensure empty lines are one line-height tall. */
           content: '\\A';
@@ -1410,23 +1412,6 @@
     );
   }
 
-  /**
-   * Gets or creates a comment thread group for a specific line and side on a
-   * diff.
-   * Private but used in tests.
-   */
-  getOrCreateThreadGroup(contentEl: Element, commentSide: Side) {
-    // Check if thread group exists.
-    let threadGroupEl = contentEl.querySelector('.thread-group');
-    if (!threadGroupEl) {
-      threadGroupEl = document.createElement('div');
-      threadGroupEl.className = 'thread-group';
-      threadGroupEl.setAttribute('data-side', commentSide);
-      contentEl.appendChild(threadGroupEl);
-    }
-    return threadGroupEl;
-  }
-
   private getCommentSideByLineAndContent(
     lineEl: Element,
     contentEl: Element
@@ -1696,7 +1681,6 @@
       if (lineNum === 'LOST') {
         this.insertPortedCommentsWithoutRangeMessage(contentEl);
       }
-      const threadGroupEl = this.getOrCreateThreadGroup(contentEl, commentSide);
 
       const slotAtt = threadEl.getAttribute('slot');
       if (range && isLongCommentRange(range) && slotAtt) {
@@ -1709,16 +1693,6 @@
         this.insertBefore(longRangeCommentHint, threadEl);
         this.redispatchHoverEvents(longRangeCommentHint, threadEl);
       }
-
-      // Create a slot for the thread and attach it to the thread group.
-      // The Polyfill has some bugs and this only works if the slot is
-      // attached to the group after the group is attached to the DOM.
-      // The thread group may already have a slot with the right name, but
-      // that is okay because the first matching slot is used and the rest
-      // are ignored.
-      const slot = document.createElement('slot');
-      if (slotAtt) slot.name = slotAtt;
-      threadGroupEl.appendChild(slot);
     }
 
     for (const threadEl of removedThreadEls) {
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 00e46c7..1db3945 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
@@ -6,7 +6,6 @@
 import '../../../test/common-test-setup';
 import {createDiff} from '../../../test/test-data-generators';
 import './gr-diff';
-import {GrDiffBuilderImage} from '../gr-diff-builder/gr-diff-builder-image';
 import {getComputedStyleValue} from '../../../utils/dom-util';
 import '@polymer/paper-button/paper-button';
 import {
@@ -108,14 +107,14 @@
                   <td class="blame gr-diff" data-line-number="LOST"></td>
                   <td class="gr-diff left lineNum" data-value="LOST"></td>
                   <td class="gr-diff left no-intraline-info sign"></td>
-                  <td
-                    class="both content gr-diff left lost no-intraline-info"
-                  ></td>
+                  <td class="both content gr-diff left lost no-intraline-info">
+                    <div class="thread-group" data-side="left"></div>
+                  </td>
                   <td class="gr-diff lineNum right" data-value="LOST"></td>
                   <td class="gr-diff no-intraline-info right sign"></td>
-                  <td
-                    class="both content gr-diff lost no-intraline-info right"
-                  ></td>
+                  <td class="both content gr-diff lost no-intraline-info right">
+                    <div class="thread-group" data-side="right"></div>
+                  </td>
                 </tr>
               </tbody>
               <tbody class="both gr-diff section">
@@ -138,9 +137,9 @@
                     </button>
                   </td>
                   <td class="gr-diff left no-intraline-info sign"></td>
-                  <td
-                    class="both content file gr-diff left no-intraline-info"
-                  ></td>
+                  <td class="both content file gr-diff left no-intraline-info">
+                    <div class="thread-group" data-side="left"></div>
+                  </td>
                   <td class="gr-diff lineNum right" data-value="FILE">
                     <button
                       aria-label="Add file comment"
@@ -153,9 +152,9 @@
                     </button>
                   </td>
                   <td class="gr-diff no-intraline-info right sign"></td>
-                  <td
-                    class="both content file gr-diff no-intraline-info right"
-                  ></td>
+                  <td class="both content file gr-diff no-intraline-info right">
+                    <div class="thread-group" data-side="right"></div>
+                  </td>
                 </tr>
               </tbody>
               <tbody class="both gr-diff section">
@@ -184,9 +183,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-1"
-                    >
-                      Lorem ipsum dolor sit amet, suspendisse inceptos vehicula.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="1">
                     <button
@@ -205,9 +203,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-1"
-                    >
-                      Lorem ipsum dolor sit amet, suspendisse inceptos vehicula.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -235,9 +232,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-2"
-                    >
-                      Mattis lectus.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="2">
                     <button
@@ -256,9 +252,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-2"
-                    >
-                      Mattis lectus.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -286,9 +281,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-3"
-                    >
-                      Sodales duis.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="3">
                     <button
@@ -307,9 +301,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-3"
-                    >
-                      Sodales duis.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -337,9 +330,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-4"
-                    >
-                      Orci a faucibus.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="4">
                     <button
@@ -358,9 +350,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-4"
-                    >
-                      Orci a faucibus.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
               </tbody>
@@ -399,9 +390,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-5"
-                    >
-                      Nullam neque, ligula ac, id blandit.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -438,9 +428,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-6"
-                    >
-                      Sagittis tincidunt torquent, tempor nunc amet.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -477,9 +466,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-7"
-                    >
-                      At rhoncus id.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
               </tbody>
@@ -509,9 +497,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-5"
-                    >
-                      Sem nascetur, erat ut, non in.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="8">
                     <button
@@ -530,9 +517,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-8"
-                    >
-                      Sem nascetur, erat ut, non in.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -560,9 +546,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-6"
-                    >
-                      A donec, venenatis pellentesque dis.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="9">
                     <button
@@ -581,9 +566,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-9"
-                    >
-                      A donec, venenatis pellentesque dis.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -611,9 +595,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-7"
-                    >
-                      Mauris mauris.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="10">
                     <button
@@ -632,9 +615,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-10"
-                    >
-                      Mauris mauris.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -662,9 +644,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-8"
-                    >
-                      Quisque nisl duis, facilisis viverra.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="11">
                     <button
@@ -683,9 +664,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-11"
-                    >
-                      Quisque nisl duis, facilisis viverra.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -713,9 +693,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-9"
-                    >
-                      Justo purus, semper eget et.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="12">
                     <button
@@ -734,9 +713,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-12"
-                    >
-                      Justo purus, semper eget et.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
               </tbody>
@@ -766,9 +744,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-10"
-                    >
-                      Est amet, vestibulum pellentesque.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="blankLineNum gr-diff right"></td>
                   <td class="blank gr-diff no-intraline-info right sign"></td>
@@ -805,9 +782,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-11"
-                    >
-                      Erat ligula.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="blankLineNum gr-diff right"></td>
                   <td class="blank gr-diff no-intraline-info right sign"></td>
@@ -844,9 +820,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-12"
-                    >
-                      Justo eros.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="blankLineNum gr-diff right"></td>
                   <td class="blank gr-diff no-intraline-info right sign"></td>
@@ -883,9 +858,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-13"
-                    >
-                      Fringilla quisque.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="blankLineNum gr-diff right"></td>
                   <td class="blank gr-diff no-intraline-info right sign"></td>
@@ -924,9 +898,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-14"
-                    >
-                      Arcu eget, rhoncus amet cursus, ipsum elementum.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="13">
                     <button
@@ -945,9 +918,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-13"
-                    >
-                      Arcu eget, rhoncus amet cursus, ipsum elementum.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -975,9 +947,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-15"
-                    >
-                      Eros suspendisse.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="14">
                     <button
@@ -996,9 +967,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-14"
-                    >
-                      Eros suspendisse.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
               </tbody>
@@ -1028,10 +998,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-16"
-                    >
-                      Rhoncus tempor, ultricies
-                      <hl class="gr-diff intraline"> aliquam </hl> ipsum.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="15">
                     <button
@@ -1050,10 +1018,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-15"
-                    >
-                      Rhoncus tempor, ultricies
-                      <hl class="gr-diff intraline"> praesent </hl> ipsum.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
               </tbody>
@@ -1083,9 +1049,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-17"
-                    >
-                      Sollicitudin duis.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="16">
                     <button
@@ -1104,9 +1069,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-16"
-                    >
-                      Sollicitudin duis.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1134,9 +1098,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-18"
-                    >
-                      Blandit blandit, ante nisl fusce.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="17">
                     <button
@@ -1155,9 +1118,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-17"
-                    >
-                      Blandit blandit, ante nisl fusce.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1185,9 +1147,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-19"
-                    >
-                      Felis ac at, tellus consectetuer.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="18">
                     <button
@@ -1206,9 +1167,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-18"
-                    >
-                      Felis ac at, tellus consectetuer.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
               </tbody>
@@ -1276,9 +1236,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-38"
-                    >
-                      Ullamcorper nunc ante, nec imperdiet felis, consectetur.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="37">
                     <button
@@ -1297,9 +1256,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-37"
-                    >
-                      Ullamcorper nunc ante, nec imperdiet felis, consectetur.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1327,9 +1285,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-39"
-                    >
-                      Ac eget.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="38">
                     <button
@@ -1348,9 +1305,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-38"
-                    >
-                      Ac eget.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1378,9 +1334,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-40"
-                    >
-                      Vel fringilla, interdum pellentesque placerat, proin ante.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="39">
                     <button
@@ -1399,9 +1354,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-39"
-                    >
-                      Vel fringilla, interdum pellentesque placerat, proin ante.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
               </tbody>
@@ -1440,9 +1394,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-40"
-                    >
-                      Eu congue risus.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1479,9 +1432,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-41"
-                    >
-                      Enim ac, quis elementum.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1518,9 +1470,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-42"
-                    >
-                      Non et elit.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1557,9 +1508,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-43"
-                    >
-                      Etiam aliquam, diam vel nunc.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
               </tbody>
@@ -1589,9 +1539,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-41"
-                    >
-                      Nec at.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="44">
                     <button
@@ -1610,9 +1559,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-44"
-                    >
-                      Nec at.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1640,9 +1588,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-42"
-                    >
-                      Arcu mauris, venenatis lacus fermentum, praesent duis.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="45">
                     <button
@@ -1661,9 +1608,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-45"
-                    >
-                      Arcu mauris, venenatis lacus fermentum, praesent duis.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1691,9 +1637,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-43"
-                    >
-                      Pellentesque amet et, tellus duis.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="46">
                     <button
@@ -1712,9 +1657,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-46"
-                    >
-                      Pellentesque amet et, tellus duis.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1742,9 +1686,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-44"
-                    >
-                      Ipsum arcu vitae, justo elit, sed libero tellus.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="47">
                     <button
@@ -1763,9 +1706,8 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-47"
-                    >
-                      Ipsum arcu vitae, justo elit, sed libero tellus.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
                 <tr
@@ -1793,9 +1735,8 @@
                       class="contentText gr-diff"
                       data-side="left"
                       id="left-content-45"
-                    >
-                      Metus rutrum euismod, vivamus sodales, vel arcu nisl.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="left"></div>
                   </td>
                   <td class="gr-diff lineNum right" data-value="48">
                     <button
@@ -1814,15 +1755,17 @@
                       class="contentText gr-diff"
                       data-side="right"
                       id="right-content-48"
-                    >
-                      Metus rutrum euismod, vivamus sodales, vel arcu nisl.
-                    </div>
+                    ></div>
+                    <div class="thread-group" data-side="right"></div>
                   </td>
                 </tr>
               </tbody>
             </table>
           </div>
-        `
+        `,
+        {
+          ignoreTags: ['gr-legacy-text', 'slot'],
+        }
       );
     };
   });
@@ -1969,25 +1912,6 @@
       assert.isTrue(container.classList.contains('displayLine'));
     });
 
-    test('thread groups', () => {
-      const contentEl = document.createElement('div');
-
-      element.path = 'file.txt';
-
-      // No thread groups.
-      assert.equal(contentEl.querySelectorAll('.thread-group').length, 0);
-
-      // A thread group gets created.
-      const threadGroupEl = element.getOrCreateThreadGroup(
-        contentEl,
-        Side.LEFT
-      );
-      assert.isOk(threadGroupEl);
-
-      // The new thread group can be fetched.
-      assert.equal(contentEl.querySelectorAll('.thread-group').length, 1);
-    });
-
     suite('image diffs', () => {
       let mockFile1: ImageInfo;
       let mockFile2: ImageInfo;
@@ -2021,7 +1945,7 @@
         };
       });
 
-      test('renders image diffs with same file name', async () => {
+      test('render image diff', async () => {
         element.baseImage = mockFile1;
         element.revisionImage = mockFile2;
         element.diff = {
@@ -2039,39 +1963,46 @@
           content: [{skip: 66}],
           binary: true,
         };
+
         await waitForEventOnce(element, 'render');
-
-        // Recognizes that it should be an image diff.
-        assert.isTrue(element.isImageDiff);
-        assert.instanceOf(element.diffBuilder.builder, GrDiffBuilderImage);
-
-        // Left image rendered with the parent commit's version of the file.
-        assertIsDefined(element.diffTable);
-        const diffTable = element.diffTable;
-        const leftImage = queryAndAssert(diffTable, 'td.left img');
-        const leftLabel = queryAndAssert(diffTable, 'td.left label');
-        const leftLabelContent = queryAndAssert(leftLabel, '.label');
-        const leftLabelName = query(leftLabel, '.name');
-
-        const rightImage = queryAndAssert(diffTable, 'td.right img');
-        const rightLabel = queryAndAssert(diffTable, 'td.right label');
-        const rightLabelContent = queryAndAssert(rightLabel, '.label');
-        const rightLabelName = query(rightLabel, '.name');
-
-        assert.isNotOk(rightLabelName);
-        assert.isNotOk(leftLabelName);
-
-        assert.equal(
-          leftImage.getAttribute('src'),
-          'data:image/bmp;base64,' + mockFile1.body
+        const imageDiffSection = queryAndAssert(element, 'tbody.image-diff');
+        assert.lightDom.equal(
+          imageDiffSection,
+          /* HTML */ `
+            <tbody class="gr-diff image-diff">
+              <tr class="gr-diff">
+                <td class="blank gr-diff left lineNum"></td>
+                <td class="gr-diff left">
+                  <img
+                    class="gr-diff"
+                    src="data:image/bmp;base64,${mockFile1.body}"
+                  />
+                </td>
+                <td class="blank gr-diff lineNum right"></td>
+                <td class="gr-diff right">
+                  <img
+                    class="gr-diff"
+                    src="data:image/bmp;base64,${mockFile2.body}"
+                  />
+                </td>
+              </tr>
+              <tr class="gr-diff">
+                <td class="blank gr-diff left lineNum"></td>
+                <td class="gr-diff left">
+                  <label class="gr-diff">
+                    <span class="gr-diff label"> image/bmp </span>
+                  </label>
+                </td>
+                <td class="blank gr-diff lineNum right"></td>
+                <td class="gr-diff right">
+                  <label class="gr-diff">
+                    <span class="gr-diff label"> image/bmp </span>
+                  </label>
+                </td>
+              </tr>
+            </tbody>
+          `
         );
-        assert.isTrue(leftLabelContent.textContent?.includes('image/bmp'));
-
-        assert.equal(
-          rightImage.getAttribute('src'),
-          'data:image/bmp;base64,' + mockFile2.body
-        );
-        assert.isTrue(rightLabelContent.textContent?.includes('image/bmp'));
       });
 
       test('renders image diffs with a different file name', async () => {
@@ -2096,43 +2027,31 @@
         element.revisionImage = mockFile2;
         element.revisionImage._name = mockDiff.meta_b!.name;
         element.diff = mockDiff;
+
         await waitForEventOnce(element, 'render');
-
-        // Recognizes that it should be an image diff.
-        assert.isTrue(element.isImageDiff);
-        assert.instanceOf(element.diffBuilder.builder, GrDiffBuilderImage);
-
-        // Left image rendered with the parent commit's version of the file.
-        assertIsDefined(element.diffTable);
-        const diffTable = element.diffTable;
-        const leftImage = queryAndAssert(diffTable, 'td.left img');
-        const leftLabel = queryAndAssert(diffTable, 'td.left label');
-        const leftLabelContent = queryAndAssert(leftLabel, '.label');
-        const leftLabelName = queryAndAssert(leftLabel, '.name');
-
-        const rightImage = queryAndAssert(diffTable, 'td.right img');
-        const rightLabel = queryAndAssert(diffTable, 'td.right label');
-        const rightLabelContent = queryAndAssert(rightLabel, '.label');
-        const rightLabelName = queryAndAssert(rightLabel, '.name');
-
-        assert.isOk(rightLabelName);
-        assert.isOk(leftLabelName);
-        assert.equal(leftLabelName.textContent, mockDiff.meta_a?.name);
-        assert.equal(rightLabelName.textContent, mockDiff.meta_b?.name);
-
-        assert.isOk(leftImage);
-        assert.equal(
-          leftImage.getAttribute('src'),
-          'data:image/bmp;base64,' + mockFile1.body
+        const imageDiffSection = queryAndAssert(element, 'tbody.image-diff');
+        const leftLabel = queryAndAssert(imageDiffSection, 'td.left label');
+        const rightLabel = queryAndAssert(imageDiffSection, 'td.right label');
+        assert.dom.equal(
+          leftLabel,
+          /* HTML */ `
+            <label class="gr-diff">
+              <span class="gr-diff name"> carrot.jpg </span>
+              <br class="gr-diff" />
+              <span class="gr-diff label"> image/bmp </span>
+            </label>
+          `
         );
-        assert.isTrue(leftLabelContent.textContent?.includes('image/bmp'));
-
-        assert.isOk(rightImage);
-        assert.equal(
-          rightImage.getAttribute('src'),
-          'data:image/bmp;base64,' + mockFile2.body
+        assert.dom.equal(
+          rightLabel,
+          /* HTML */ `
+            <label class="gr-diff">
+              <span class="gr-diff name"> carrot2.jpg </span>
+              <br class="gr-diff" />
+              <span class="gr-diff label"> image/bmp </span>
+            </label>
+          `
         );
-        assert.isTrue(rightLabelContent.textContent?.includes('image/bmp'));
       });
 
       test('renders added image', async () => {
@@ -2150,26 +2069,23 @@
           content: [{skip: 66}],
           binary: true,
         };
-
-        const promise = mockPromise();
-        function rendered() {
-          promise.resolve();
-        }
-        element.addEventListener('render', rendered);
-
         element.revisionImage = mockFile2;
         element.diff = mockDiff;
-        await promise;
-        element.removeEventListener('render', rendered);
-        // Recognizes that it should be an image diff.
-        assert.isTrue(element.isImageDiff);
-        assert.instanceOf(element.diffBuilder.builder, GrDiffBuilderImage);
 
-        assertIsDefined(element.diffTable);
-        const diffTable = element.diffTable;
-        const leftImage = query(diffTable, 'td.left img');
+        await waitForEventOnce(element, 'render');
+        const imageDiffSection = queryAndAssert(element, 'tbody.image-diff');
+        const leftImage = query(imageDiffSection, 'td.left img');
+        const rightImage = queryAndAssert(imageDiffSection, 'td.right img');
         assert.isNotOk(leftImage);
-        queryAndAssert(diffTable, 'td.right img');
+        assert.dom.equal(
+          rightImage,
+          /* HTML */ `
+            <img
+              class="gr-diff"
+              src="data:image/bmp;base64,${mockFile2.body}"
+            />
+          `
+        );
       });
 
       test('renders removed image', async () => {
@@ -2187,25 +2103,23 @@
           content: [{skip: 66}],
           binary: true,
         };
-        const promise = mockPromise();
-        function rendered() {
-          promise.resolve();
-        }
-        element.addEventListener('render', rendered);
-
         element.baseImage = mockFile1;
         element.diff = mockDiff;
-        await promise;
-        element.removeEventListener('render', rendered);
-        // Recognizes that it should be an image diff.
-        assert.isTrue(element.isImageDiff);
-        assert.instanceOf(element.diffBuilder.builder, GrDiffBuilderImage);
 
-        assertIsDefined(element.diffTable);
-        const diffTable = element.diffTable;
-        queryAndAssert(diffTable, 'td.left img');
-        const rightImage = query(diffTable, 'td.right img');
+        await waitForEventOnce(element, 'render');
+        const imageDiffSection = queryAndAssert(element, 'tbody.image-diff');
+        const leftImage = queryAndAssert(imageDiffSection, 'td.left img');
+        const rightImage = query(imageDiffSection, 'td.right img');
         assert.isNotOk(rightImage);
+        assert.dom.equal(
+          leftImage,
+          /* HTML */ `
+            <img
+              class="gr-diff"
+              src="data:image/bmp;base64,${mockFile1.body}"
+            />
+          `
+        );
       });
 
       test('does not render disallowed image type', async () => {
@@ -2228,23 +2142,12 @@
           binary: true,
         };
         mockFile1.type = 'image/jpeg-evil';
-
-        const promise = mockPromise();
-        function rendered() {
-          promise.resolve();
-        }
-        element.addEventListener('render', rendered);
-
         element.baseImage = mockFile1;
         element.diff = mockDiff;
-        await promise;
-        element.removeEventListener('render', rendered);
-        // Recognizes that it should be an image diff.
-        assert.isTrue(element.isImageDiff);
-        assert.instanceOf(element.diffBuilder.builder, GrDiffBuilderImage);
-        assertIsDefined(element.diffTable);
-        const diffTable = element.diffTable;
-        const leftImage = query(diffTable, 'td.left img');
+
+        await waitForEventOnce(element, 'render');
+        const imageDiffSection = queryAndAssert(element, 'tbody.image-diff');
+        const leftImage = query(imageDiffSection, 'td.left img');
         assert.isNotOk(leftImage);
       });
     });
@@ -2582,45 +2485,30 @@
 
     test('large render w/ context = 10', async () => {
       element.prefs = {...MINIMAL_PREFS, context: 10};
-      const promise = mockPromise();
-      function rendered() {
-        assert.isTrue(renderStub.called);
-        assert.isFalse(element.showWarning);
-        promise.resolve();
-        element.removeEventListener('render', rendered);
-      }
-      element.addEventListener('render', rendered);
       element.renderDiffTable();
-      await promise;
+      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;
-      const promise = mockPromise();
-      function rendered() {
-        assert.isTrue(renderStub.called);
-        assert.isFalse(element.showWarning);
-        promise.resolve();
-        element.removeEventListener('render', rendered);
-      }
-      element.addEventListener('render', rendered);
       element.renderDiffTable();
-      await promise;
+      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};
-      const promise = mockPromise();
-      function rendered() {
-        assert.isFalse(renderStub.called);
-        assert.isTrue(element.showWarning);
-        promise.resolve();
-        element.removeEventListener('render', rendered);
-      }
-      element.addEventListener('render', rendered);
       element.renderDiffTable();
-      await promise;
+      await waitForEventOnce(element, 'render');
+
+      assert.isFalse(renderStub.called);
+      assert.isTrue(element.showWarning);
     });
 
     test('toggles expand context using bypass', async () => {