Merge "Display AI-powered checks with dedicated styling."
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
index 4bbd8be..42a5f8e 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary.ts
@@ -514,9 +514,11 @@
       (sum, run) => sum + (resultFilter(run).length || 1),
       0
     );
+    const hasAiPowered = runs.some(run => run.isAiPowered);
     if (count === 0) return;
     const handler = () => this.onChipClick({statusOrCategory});
     return html`<gr-checks-chip
+      .isAiPowered=${hasAiPowered}
       .statusOrCategory=${statusOrCategory}
       .text=${`${count}`}
       @click=${handler}
@@ -549,6 +551,7 @@
     const links = [];
     if (run.statusLink) links.push(run.statusLink);
     const text = `${run.checkName}`;
+    const isAiPowered = run.isAiPowered;
     const tabState: ChecksTabState = {
       checkName: run.checkName,
       statusOrCategory,
@@ -564,6 +567,7 @@
     const handler = () => this.onChipClick(tabState);
     return html`<gr-checks-chip
       .statusOrCategory=${statusOrCategory}
+      .isAiPowered=${isAiPowered}
       .text=${text}
       .links=${links}
       @click=${handler}
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_screenshot_test.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_screenshot_test.ts
index ed3e0d0..8a537d9 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_screenshot_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-change-summary_screenshot_test.ts
@@ -97,4 +97,22 @@
       'gr-change-summary-with-ai-review-prompt'
     );
   });
+
+  test('screenshot with AI powered check runs', async () => {
+    element.runs = [
+      createRun({
+        status: RunStatus.COMPLETED,
+        checkName: 'ai-warning-check',
+        isAiPowered: true,
+        results: [createCheckResult({category: Category.WARNING})],
+      }),
+    ];
+    await element.updateComplete;
+
+    await visualDiff(element, 'gr-change-summary-with-ai-powered-check-runs');
+    await visualDiffDarkTheme(
+      element,
+      'gr-change-summary-with-ai-powered-check-runs'
+    );
+  });
 });
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip.ts
index 1315385..9754382 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip.ts
@@ -28,6 +28,9 @@
   @property({type: Array})
   links: string[] = [];
 
+  @property({type: Boolean})
+  isAiPowered = false;
+
   private readonly reporting = getAppContext().reportingService;
 
   static override get styles() {
@@ -75,6 +78,7 @@
         }
         gr-icon {
           font-size: var(--line-height-small);
+          --gr-icon-size: var(--line-height-small);
         }
         .checksChip a gr-icon.launch {
           color: var(--link-color);
@@ -108,18 +112,22 @@
         .checksChip.warning gr-icon {
           color: var(--warning-foreground);
         }
-        .checksChip.info {
+        .checksChip.info,
+        .checksChip.ai {
           border-color: var(--info-foreground);
           background: var(--info-background);
         }
-        .checksChip.info:hover {
+        .checksChip.info:hover,
+        .checksChip.ai:hover {
           background: var(--info-background-hover);
           box-shadow: var(--elevation-level-1);
         }
-        .checksChip.info:focus-within {
+        .checksChip.info:focus-within,
+        .checksChip.ai:focus-within {
           background: var(--info-background-focus);
         }
-        .checksChip.info gr-icon {
+        .checksChip.info gr-icon,
+        .checksChip.ai gr-icon {
           color: var(--info-foreground);
         }
         .checksChip.check_circle {
@@ -161,7 +169,7 @@
   override render() {
     if (!this.text) return;
     if (!this.statusOrCategory) return;
-    const icon = iconFor(this.statusOrCategory);
+    const icon = iconFor(this.statusOrCategory, this.isAiPowered);
     const ariaLabel = this.computeAriaLabel();
     const chipClass = `checksChip font-small ${icon.name}`;
     const chipClassFullLength = `${chipClass} hoverFullLength`;
diff --git a/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip_test.ts b/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip_test.ts
index 6816609..f78734c 100644
--- a/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-summary/gr-checks-chip_test.ts
@@ -85,4 +85,25 @@
       `
     );
   });
+
+  test('renders ai chip', async () => {
+    element.text = 'AI Suggestion';
+    element.statusOrCategory = Category.INFO;
+    element.isAiPowered = true;
+    await element.updateComplete;
+    assert.shadowDom.equal(
+      element,
+      /* HTML */ `
+        <div
+          aria-label="info for check AI Suggestion"
+          class="checksChip ai font-small"
+          role="link"
+          tabindex="0"
+        >
+          <gr-icon icon="ai" filled></gr-icon>
+          <div class="text">AI Suggestion</div>
+        </div>
+      `
+    );
+  });
 });
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
index dc28545..6fa8b72 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.ts
@@ -712,6 +712,7 @@
           color: var(--primary-text-color);
           & gr-icon {
             font-size: var(--line-height-small);
+            --gr-icon-size: var(--line-height-small);
           }
           &.info {
             border-color: var(--info-foreground);
@@ -720,6 +721,13 @@
               color: var(--info-foreground);
             }
           }
+          &.ai {
+            border-color: var(--info-foreground);
+            background-color: var(--info-background);
+            & gr-icon {
+              color: var(--info-foreground);
+            }
+          }
           &.warning {
             border-color: var(--warning-foreground);
             background-color: var(--warning-background);
@@ -1847,7 +1855,7 @@
       ) {
         continue;
       }
-      const icon = iconFor(result.category);
+      const icon = iconFor(result.category, result.isAiPowered);
       iconsByName[icon.name] ??= [];
       iconsByName[icon.name].push(icon);
     }
diff --git a/polygerrit-ui/app/elements/checks/gr-diff-check-result.ts b/polygerrit-ui/app/elements/checks/gr-diff-check-result.ts
index 40a3c94..1f1ef11 100644
--- a/polygerrit-ui/app/elements/checks/gr-diff-check-result.ts
+++ b/polygerrit-ui/app/elements/checks/gr-diff-check-result.ts
@@ -22,7 +22,7 @@
 import './gr-hovercard-run';
 import './gr-checks-tag';
 import {fontStyles} from '../../styles/gr-font-styles';
-import {Action, Category} from '../../api/checks';
+import {Action, Category, Tag} from '../../api/checks';
 import {assertIsDefined} from '../../utils/common-util';
 import {resolve} from '../../models/dependency';
 import {commentsModelToken} from '../../models/comments/comments-model';
@@ -96,13 +96,6 @@
           padding: var(--spacing-xs) var(--spacing-m);
           border: 1px solid #888;
         }
-        .container.info {
-          border-color: var(--info-foreground);
-          background-color: var(--info-background);
-        }
-        .container.info gr-icon {
-          color: var(--info-foreground);
-        }
         .container.warning {
           border-color: var(--warning-foreground);
           background-color: var(--warning-background);
@@ -110,6 +103,17 @@
         .container.warning gr-icon {
           color: var(--warning-foreground);
         }
+        .container.info {
+          border-color: var(--info-foreground);
+          background-color: var(--info-background);
+        }
+        .container.info gr-icon {
+          color: var(--info-foreground);
+        }
+        .container .note {
+          font-style: italic;
+          margin-left: var(--spacing-s);
+        }
         .container.error {
           border-color: var(--error-foreground);
           background-color: var(--error-background);
@@ -117,6 +121,13 @@
         .container.error gr-icon {
           color: var(--error-foreground);
         }
+        .container.ai-powered {
+          border-color: var(--info-foreground);
+          background-color: var(--info-background);
+        }
+        .container.ai-powered gr-icon {
+          color: var(--info-foreground);
+        }
         .header {
           display: flex;
           white-space: nowrap;
@@ -151,9 +162,11 @@
         }
         gr-icon {
           font-size: var(--line-height-normal);
+          --gr-icon-size: var(--line-height-normal);
         }
         .icon gr-icon {
           font-size: calc(var(--line-height-normal) - 4px);
+          --gr-icon-size: calc(var(--line-height-normal) - 4px);
           position: relative;
           top: 2px;
         }
@@ -212,14 +225,14 @@
   override render() {
     if (!this.result) return;
     const cat = this.result.category.toLowerCase();
-    const icon = iconFor(this.result.category);
-    const aiIcon = this.result.isAiPowered
-      ? html`<div class="ai-icon-wrapper">
-          <gr-icon small icon="ai"></gr-icon>
-        </div>`
-      : nothing;
+    const icon = iconFor(this.result.category, this.result.isAiPowered);
     return html`
-      <div class="${cat} container font-normal" @copy=${this.handleCopy}>
+      <div
+        class="${cat} container font-normal ${this.result.isAiPowered
+          ? 'ai-powered'
+          : ''}"
+        @copy=${this.handleCopy}
+      >
         <div class="header" @click=${this.toggleExpandedClick}>
           <div class="icon">
             <gr-icon icon=${icon.name} ?filled=${!!icon.filled}></gr-icon>
@@ -232,10 +245,9 @@
               tabindex="0"
               @keydown=${this.toggleExpandedPress}
             >
-              ${this.result.checkName}
+              ${this.result.checkName}${this.renderDetail(this.result.tags)}
             </div>
           </div>
-          ${aiIcon}
           <!-- The &nbsp; is for being able to shrink a tiny amount without
                 the text itself getting shrunk with an ellipsis. -->
           <div class="summary">${this.result.summary}&nbsp;</div>
@@ -289,8 +301,21 @@
     ></gr-checks-fix-preview>`;
   }
 
+  private renderDetail(tags: Tag[] | undefined) {
+    for (const tag of tags ?? []) {
+      if (tag.name === 'Unpublished') {
+        return html`<span class="note">(${tag.name})</span>`;
+      }
+    }
+    return nothing;
+  }
+
   private renderTags() {
-    const tags = this.result?.tags ?? [];
+    // Filter out the "Unpublished" tag as it is rendered in the header via
+    // renderDetail.
+    const tags = (this.result?.tags ?? []).filter(
+      tag => tag.name !== 'Unpublished'
+    );
     return html`<div class="tags">
       ${tags.map(tag => html`<gr-checks-tag .tag=${tag}></gr-checks-tag>`)}
     </div>`;
diff --git a/polygerrit-ui/app/elements/checks/gr-diff-check-result_screenshot_test.ts b/polygerrit-ui/app/elements/checks/gr-diff-check-result_screenshot_test.ts
index 7ef3577..57cee38 100644
--- a/polygerrit-ui/app/elements/checks/gr-diff-check-result_screenshot_test.ts
+++ b/polygerrit-ui/app/elements/checks/gr-diff-check-result_screenshot_test.ts
@@ -9,6 +9,10 @@
 // @ts-ignore
 import {visualDiff} from '@web/test-runner-visual-regression';
 import {visualDiffDarkTheme} from '../../test/test-utils';
+import {testResolver} from '../../test/common-test-setup';
+import {changeModelToken} from '../../models/change/change-model';
+import {checksModelToken} from '../../models/checks/checks-model';
+import {suggestionsServiceToken} from '../../services/suggestions/suggestions-service';
 import {GrDiffCheckResult} from './gr-diff-check-result';
 import './gr-diff-check-result';
 import {checkRun1} from '../../test/test-data-generators';
@@ -18,14 +22,27 @@
   let element: GrDiffCheckResult;
 
   setup(async () => {
+    testResolver(changeModelToken);
+    testResolver(checksModelToken);
+    const suggestionsService = testResolver(suggestionsServiceToken);
+    sinon
+      .stub(suggestionsService, 'isGeneratedSuggestedFixEnabled')
+      .returns(true);
+
     element = await fixture(
       html`<gr-diff-check-result></gr-diff-check-result>`
     );
+    element.style.display = 'block';
+    element.style.width = '600px';
+    await element.updateComplete;
   });
 
   test('collapsed', async () => {
     element.result = {...checkRun1, ...checkRun1.results?.[0]} as RunResult;
+    element.result.isAiPowered = false;
     await element.updateComplete;
+    await document.fonts.ready;
+    await new Promise(resolve => setTimeout(resolve, 500));
 
     await visualDiff(element, 'gr-diff-check-result-collapsed');
     await visualDiffDarkTheme(element, 'gr-diff-check-result-collapsed');
@@ -34,9 +51,46 @@
   test('expanded', async () => {
     element.result = {...checkRun1, ...checkRun1.results?.[2]} as RunResult;
     element.isExpanded = true;
+    element.result.isAiPowered = false;
     await element.updateComplete;
+    await document.fonts.ready;
+    await new Promise(resolve => setTimeout(resolve, 500));
 
     await visualDiff(element, 'gr-diff-check-result-expanded');
     await visualDiffDarkTheme(element, 'gr-diff-check-result-expanded');
   });
+
+  test('collapsed with AI powered check results', async () => {
+    element.result = {...checkRun1, ...checkRun1.results?.[0]} as RunResult;
+    await element.updateComplete;
+
+    await visualDiff(element, 'gr-diff-check-result-collapsed-ai-powered');
+    await visualDiffDarkTheme(
+      element,
+      'gr-diff-check-result-collapsed-ai-powered'
+    );
+  });
+
+  test('expanded with AI powered check results', async () => {
+    element.result = {...checkRun1, ...checkRun1.results?.[2]} as RunResult;
+    element.isExpanded = true;
+    await element.updateComplete;
+
+    await visualDiff(element, 'gr-diff-check-result-expanded-ai-powered');
+    await visualDiffDarkTheme(
+      element,
+      'gr-diff-check-result-expanded-ai-powered'
+    );
+  });
+
+  test('with unpublished tag', async () => {
+    element.result = {
+      ...checkRun1,
+      ...checkRun1.results?.[0],
+      tags: [{name: 'Unpublished', tooltip: 'This is unpublished'}],
+    } as RunResult;
+    await element.updateComplete;
+    await visualDiff(element, 'gr-diff-check-result-unpublished');
+    await visualDiffDarkTheme(element, 'gr-diff-check-result-unpublished');
+  });
 });
diff --git a/polygerrit-ui/app/elements/checks/gr-diff-check-result_test.ts b/polygerrit-ui/app/elements/checks/gr-diff-check-result_test.ts
index 1b83b79..71e832b 100644
--- a/polygerrit-ui/app/elements/checks/gr-diff-check-result_test.ts
+++ b/polygerrit-ui/app/elements/checks/gr-diff-check-result_test.ts
@@ -74,10 +74,10 @@
     assert.shadowDom.equal(
       element,
       `
-        <div class="container font-normal warning">
+        <div class="ai-powered container font-normal warning">
           <div class="header">
             <div class="icon">
-              <gr-icon icon="warning" filled></gr-icon>
+              <gr-icon custom="" filled="" icon="ai"></gr-icon>
             </div>
             <div class="name">
               <gr-hovercard-run> </gr-hovercard-run>
@@ -85,9 +85,6 @@
                 FAKE Super Check
               </div>
             </div>
-            <div class="ai-icon-wrapper">
-              <gr-icon custom="" icon="ai" small></gr-icon>
-            </div>
             <div class="summary">We think that you could improve this.</div>
             <div class="message">
               There is a lot to be said. A lot. I say, a lot.
diff --git a/polygerrit-ui/app/models/checks/checks-util.ts b/polygerrit-ui/app/models/checks/checks-util.ts
index aaabe67..ee2eb91 100644
--- a/polygerrit-ui/app/models/checks/checks-util.ts
+++ b/polygerrit-ui/app/models/checks/checks-util.ts
@@ -268,7 +268,13 @@
   }
 }
 
-export function iconFor(catStat: Category | RunStatus): ChecksIcon {
+export function iconFor(
+  catStat: Category | RunStatus,
+  isAiPowered?: boolean
+): ChecksIcon {
+  if (isAiPowered) {
+    return {name: 'ai', filled: true};
+  }
   switch (catStat) {
     case Category.ERROR:
       return {name: 'error', filled: true};
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-powered-check-runs-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-powered-check-runs-dark.png
new file mode 100644
index 0000000..4761ba9
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-powered-check-runs-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-powered-check-runs.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-powered-check-runs.png
new file mode 100644
index 0000000..17be795
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-change-summary-with-ai-powered-check-runs.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed-ai-powered-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed-ai-powered-dark.png
new file mode 100644
index 0000000..650ab5c
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed-ai-powered-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed-ai-powered.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed-ai-powered.png
new file mode 100644
index 0000000..2141a7c
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed-ai-powered.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed-dark.png
index 1a01ce2..fc9d8fb 100644
--- a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed-dark.png
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed.png
index cd8f54b..d18f15e 100644
--- a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed.png
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-collapsed.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded-ai-powered-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded-ai-powered-dark.png
new file mode 100644
index 0000000..ee6c53f
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded-ai-powered-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded-ai-powered.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded-ai-powered.png
new file mode 100644
index 0000000..5ee62cc
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded-ai-powered.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded-dark.png
index 484426d..d505239 100644
--- a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded-dark.png
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded.png
index 49002e6..bfde038 100644
--- a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded.png
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-expanded.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-unpublished-dark.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-unpublished-dark.png
new file mode 100644
index 0000000..4006054
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-unpublished-dark.png
Binary files differ
diff --git a/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-unpublished.png b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-unpublished.png
new file mode 100644
index 0000000..fdd0c65
--- /dev/null
+++ b/polygerrit-ui/screenshots/Chromium/baseline/gr-diff-check-result-unpublished.png
Binary files differ