Add inline preview of fixes attached to check results
https://imgur.com/a/2paSjVH
http://screencast/cast/NTQzMzU4NzkzNDQyOTE4NHw1ZTIxYWEyMS03MQ
Release-Notes: skip
Google-Bug-Id: b/345156625
Change-Id: I905e525171f2b935806d18fcac6264fab073b56c
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-fix-preview.ts b/polygerrit-ui/app/elements/checks/gr-checks-fix-preview.ts
new file mode 100644
index 0000000..c13ef70
--- /dev/null
+++ b/polygerrit-ui/app/elements/checks/gr-checks-fix-preview.ts
@@ -0,0 +1,327 @@
+/**
+ * @license
+ * Copyright 2024 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import '../../embed/diff/gr-diff/gr-diff';
+import {css, html, LitElement, nothing, PropertyValues} from 'lit';
+import {customElement, property, state} from 'lit/decorators.js';
+import {getAppContext} from '../../services/app-context';
+import {EDIT, BasePatchSetNum, RepoName} from '../../types/common';
+import {anyLineTooLong} from '../../utils/diff-util';
+import {
+ DiffInfo,
+ DiffLayer,
+ DiffPreferencesInfo,
+ DiffViewMode,
+ RenderPreferences,
+} from '../../api/diff';
+import {GrSyntaxLayerWorker} from '../../embed/diff/gr-syntax-layer/gr-syntax-layer-worker';
+import {resolve} from '../../models/dependency';
+import {highlightServiceToken} from '../../services/highlight/highlight-service';
+import {
+ FixSuggestionInfo,
+ NumericChangeId,
+ PatchSetNumber,
+} from '../../api/rest-api';
+import {changeModelToken} from '../../models/change/change-model';
+import {subscribe} from '../lit/subscription-controller';
+import {DiffPreview} from '../diff/gr-apply-fix-dialog/gr-apply-fix-dialog';
+import {userModelToken} from '../../models/user/user-model';
+import {navigationToken} from '../core/gr-navigation/gr-navigation';
+import {fire} from '../../utils/event-util';
+import {createChangeUrl} from '../../models/views/change';
+import {OpenFixPreviewEventDetail} from '../../types/events';
+
+/**
+ * This component renders a <gr-diff> and an "apply fix" button and can be used
+ * when showing check results that have a fix for an easy preview and a quick
+ * apply-fix experience.
+ *
+ * There is a certain overlap with similar components for comment fixes:
+ * GrSuggestionDiffPreview also renders a <gr-diff> and fetches a diff preview,
+ * it relies on a `comment` (and the comment model) to be available. It supports
+ * both a `string` fix and `FixSuggestionInfo`. It also differs in logging and
+ * event handling. And it misses the header component that we need for the
+ * buttons.
+ *
+ * There is also `GrUserSuggestionsFix` which wraps `GrSuggestionDiffPreview`
+ * and has the header that we also need. But it is very targeted to be used for
+ * user suggestions and inside comments.
+ *
+ * So there is certainly an opportunity for cleanup and unification, but at the
+ * time of component creation it did not feel wortwhile investing into this
+ * effort. This is tracked in b/360288262.
+ */
+@customElement('gr-checks-fix-preview')
+export class GrChecksFixPreview extends LitElement {
+ @property({type: Object})
+ fixSuggestionInfo?: FixSuggestionInfo;
+
+ @property({type: Number})
+ patchSet?: PatchSetNumber;
+
+ @state()
+ layers: DiffLayer[] = [];
+
+ @state()
+ repo?: RepoName;
+
+ @state()
+ changeNum?: NumericChangeId;
+
+ @state()
+ latestPatchNum?: PatchSetNumber;
+
+ @state()
+ diff?: DiffPreview;
+
+ @state()
+ applyingFix = false;
+
+ @state()
+ diffPrefs?: DiffPreferencesInfo;
+
+ @state()
+ renderPrefs: RenderPreferences = {
+ disable_context_control_buttons: true,
+ show_file_comment_button: false,
+ hide_line_length_indicator: true,
+ };
+
+ private readonly restApiService = getAppContext().restApiService;
+
+ private readonly getChangeModel = resolve(this, changeModelToken);
+
+ private readonly getUserModel = resolve(this, userModelToken);
+
+ private readonly getNavigation = resolve(this, navigationToken);
+
+ private readonly syntaxLayer = new GrSyntaxLayerWorker(
+ resolve(this, highlightServiceToken),
+ () => getAppContext().reportingService
+ );
+
+ constructor() {
+ super();
+ subscribe(
+ this,
+ () => this.getChangeModel().changeNum$,
+ changeNum => (this.changeNum = changeNum)
+ );
+ subscribe(
+ this,
+ () => this.getChangeModel().latestPatchNum$,
+ x => (this.latestPatchNum = x)
+ );
+ subscribe(
+ this,
+ () => this.getUserModel().diffPreferences$,
+ diffPreferences => {
+ if (!diffPreferences) return;
+ this.diffPrefs = diffPreferences;
+ this.syntaxLayer.setEnabled(!!this.diffPrefs.syntax_highlighting);
+ }
+ );
+ subscribe(
+ this,
+ () => this.getChangeModel().repo$,
+ x => (this.repo = x)
+ );
+ }
+
+ static override get styles() {
+ return [
+ css`
+ :host {
+ display: block;
+ }
+ .header {
+ background-color: var(--background-color-primary);
+ border: 1px solid var(--border-color);
+ border-bottom: none;
+ padding: var(--spacing-xs) var(--spacing-xl);
+ display: flex;
+ align-items: center;
+ }
+ .header .title {
+ flex: 1;
+ }
+ .diff-container {
+ border: 1px solid var(--border-color);
+ border-top: none;
+ border-bottom: none;
+ }
+ .loading {
+ border: 1px solid var(--border-color);
+ padding: var(--spacing-xl);
+ }
+ `,
+ ];
+ }
+
+ override willUpdate(changed: PropertyValues) {
+ if (changed.has('fixSuggestionInfo')) {
+ this.fetchDiffPreview().then(diff => (this.diff = diff));
+ }
+ }
+
+ override render() {
+ if (!this.fixSuggestionInfo) return nothing;
+ return html`${this.renderHeader()}${this.renderDiff()}`;
+ }
+
+ private renderHeader() {
+ return html`
+ <div class="header">
+ <div class="title">
+ <span>Attached Fix</span>
+ </div>
+ <div>
+ <gr-button
+ class="showFix"
+ secondary
+ flatten
+ .disabled=${!this.diff}
+ @click=${this.showFix}
+ >
+ Show fix side-by-side
+ </gr-button>
+ <gr-button
+ class="applyFix"
+ primary
+ flatten
+ .loading=${this.applyingFix}
+ .disabled=${this.isApplyEditDisabled()}
+ @click=${this.applyFix}
+ .title=${this.computeApplyFixTooltip()}
+ >
+ Apply fix
+ </gr-button>
+ </div>
+ </div>
+ `;
+ }
+
+ private renderDiff() {
+ if (!this.diff) {
+ return html`<div class="loading">Loading fix preview ...</div>`;
+ }
+ const diff = this.diff.preview;
+ if (!anyLineTooLong(diff)) {
+ this.syntaxLayer.process(diff);
+ }
+ return html`
+ <div class="diff-container">
+ <gr-diff
+ .prefs=${this.getDiffPrefs()}
+ .path=${this.diff.filepath}
+ .diff=${diff}
+ .layers=${this.layers}
+ .renderPrefs=${this.renderPrefs}
+ .viewMode=${DiffViewMode.UNIFIED}
+ ></gr-diff>
+ </div>
+ `;
+ }
+
+ /**
+ * Calls the REST API to convert the fix into a DiffInfo.
+ */
+ private async fetchDiffPreview(): Promise<DiffPreview | undefined> {
+ if (!this.changeNum || !this.patchSet || !this.fixSuggestionInfo) return;
+ const pathsToDiffs: {[path: string]: DiffInfo} | undefined =
+ await this.restApiService.getFixPreview(
+ this.changeNum,
+ this.patchSet,
+ this.fixSuggestionInfo.replacements
+ );
+
+ if (!pathsToDiffs) return;
+ const diffs = Object.keys(pathsToDiffs).map(filepath => {
+ const diff = pathsToDiffs[filepath];
+ return {filepath, preview: diff};
+ });
+ // Showing diff for one file only.
+ return diffs?.[0];
+ }
+
+ private showFix() {
+ if (!this.patchSet || !this.fixSuggestionInfo) return;
+ const eventDetail: OpenFixPreviewEventDetail = {
+ patchNum: this.patchSet,
+ fixSuggestions: [this.fixSuggestionInfo],
+ onCloseFixPreviewCallbacks: [],
+ };
+ fire(this, 'open-fix-preview', eventDetail);
+ }
+
+ /**
+ * Applies the fix and then navigates to the EDIT patchset.
+ */
+ private async applyFix() {
+ const changeNum = this.changeNum;
+ const basePatchNum = this.patchSet as BasePatchSetNum;
+ if (!changeNum || !basePatchNum || !this.fixSuggestionInfo) return;
+
+ this.applyingFix = true;
+ const res = await this.restApiService.applyFixSuggestion(
+ changeNum,
+ basePatchNum,
+ this.fixSuggestionInfo.replacements
+ );
+ this.applyingFix = false;
+ if (res?.ok) this.navigateToEditPatchset();
+ }
+
+ private navigateToEditPatchset() {
+ const changeNum = this.changeNum;
+ const repo = this.repo;
+ const basePatchNum = this.patchSet;
+ if (!changeNum || !repo || !basePatchNum) return;
+
+ const url = createChangeUrl({
+ changeNum,
+ repo,
+ patchNum: EDIT,
+ basePatchNum,
+ // We have to force reload, because the EDIT patchset is otherwise not yet known.
+ forceReload: true,
+ });
+ this.getNavigation().setUrl(url);
+ }
+
+ /**
+ * We have to override some diff prefs of the user, because for example in the context of showing
+ * an inline diff for fixes we do not want to show context lines around the changes lines of code
+ * as we would normally do for a diff.
+ */
+ private getDiffPrefs() {
+ if (!this.diffPrefs) return undefined;
+ return {
+ ...this.diffPrefs,
+ context: 0,
+ line_length: Math.min(this.diffPrefs.line_length, 100),
+ line_wrapping: true,
+ };
+ }
+
+ private isApplyEditDisabled() {
+ if (!this.diff || this.patchSet === undefined) return true;
+ return this.patchSet !== this.latestPatchNum;
+ }
+
+ private computeApplyFixTooltip() {
+ if (this.patchSet === undefined) return '';
+ if (!this.diff) return 'Fix is still loading ...';
+ return this.patchSet !== this.latestPatchNum
+ ? 'You cannot apply this fix because it is from a previous patchset'
+ : '';
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ 'gr-checks-fix-preview': GrChecksFixPreview;
+ }
+}
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-fix-preview_test.ts b/polygerrit-ui/app/elements/checks/gr-checks-fix-preview_test.ts
new file mode 100644
index 0000000..21fa5d6
--- /dev/null
+++ b/polygerrit-ui/app/elements/checks/gr-checks-fix-preview_test.ts
@@ -0,0 +1,171 @@
+/**
+ * @license
+ * Copyright 2024 Google LLC
+ * SPDX-License-Identifier: Apache-2.0
+ */
+import '../../test/common-test-setup';
+import './gr-checks-results';
+import {html} from 'lit';
+import {fixture, assert} from '@open-wc/testing';
+import {createCheckFix, createDiff} from '../../test/test-data-generators';
+import {GrChecksFixPreview} from './gr-checks-fix-preview';
+import {rectifyFix} from '../../models/checks/checks-util';
+import {
+ MockPromise,
+ mockPromise,
+ queryAndAssert,
+ stubRestApi,
+ waitUntil,
+} from '../../test/test-utils';
+import {NumericChangeId, PatchSetNumber, RepoName} from '../../api/rest-api';
+import {FilePathToDiffInfoMap} from '../../types/common';
+import {testResolver} from '../../test/common-test-setup';
+import {navigationToken} from '../core/gr-navigation/gr-navigation';
+
+suite('gr-checks-fix-preview test', () => {
+ let element: GrChecksFixPreview;
+ let promise: MockPromise<FilePathToDiffInfoMap | undefined>;
+
+ setup(async () => {
+ promise = mockPromise<FilePathToDiffInfoMap | undefined>();
+ stubRestApi('getFixPreview').returns(promise);
+
+ const fix = rectifyFix(createCheckFix(), 'test-checker');
+ element = await fixture<GrChecksFixPreview>(
+ html`<gr-checks-fix-preview></gr-checks-fix-preview>`
+ );
+ await element.updateComplete;
+
+ element.changeNum = 123 as NumericChangeId;
+ element.patchSet = 5 as PatchSetNumber;
+ element.latestPatchNum = 5 as PatchSetNumber;
+ element.repo = 'test-repo' as RepoName;
+ element.fixSuggestionInfo = fix;
+ await element.updateComplete;
+ });
+
+ const loadDiff = async () => {
+ promise.resolve({'foo.c': createDiff()});
+ await waitUntil(() => !!element.diff);
+ };
+
+ test('renders loading', async () => {
+ assert.shadowDom.equal(
+ element,
+ /* HTML */ `
+ <div class="header">
+ <div class="title">
+ <span> Attached Fix </span>
+ </div>
+ <div>
+ <gr-button
+ class="showFix"
+ aria-disabled="true"
+ disabled=""
+ flatten=""
+ role="button"
+ secondary=""
+ tabindex="-1"
+ >
+ Show fix side-by-side
+ </gr-button>
+ <gr-button
+ class="applyFix"
+ aria-disabled="true"
+ disabled=""
+ flatten=""
+ primary=""
+ role="button"
+ tabindex="-1"
+ title="Fix is still loading ..."
+ >
+ Apply fix
+ </gr-button>
+ </div>
+ </div>
+ <div class="loading">Loading fix preview ...</div>
+ `
+ );
+ });
+
+ test('renders diff', async () => {
+ await loadDiff();
+ assert.shadowDom.equal(
+ element,
+ /* HTML */ `
+ <div class="header">
+ <div class="title">
+ <span> Attached Fix </span>
+ </div>
+ <div>
+ <gr-button
+ class="showFix"
+ aria-disabled="false"
+ flatten=""
+ role="button"
+ secondary=""
+ tabindex="0"
+ >
+ Show fix side-by-side
+ </gr-button>
+ <gr-button
+ class="applyFix"
+ aria-disabled="false"
+ flatten=""
+ primary=""
+ role="button"
+ tabindex="0"
+ title=""
+ >
+ Apply fix
+ </gr-button>
+ </div>
+ </div>
+ <div class="diff-container">
+ <gr-diff
+ class="disable-context-control-buttons hide-line-length-indicator"
+ style="--line-limit-marker: 100ch; --content-width: none; --diff-max-width: none; --font-size: 12px;"
+ >
+ </gr-diff>
+ </div>
+ `
+ );
+ });
+
+ test('show-fix', async () => {
+ await loadDiff();
+
+ const stub = sinon.stub();
+ element.addEventListener('open-fix-preview', stub);
+
+ const button = queryAndAssert<HTMLElement>(element, 'gr-button.showFix');
+ assert.isFalse(button.hasAttribute('disabled'));
+ button.click();
+
+ assert.isTrue(stub.called);
+ assert.deepEqual(stub.lastCall.args[0].detail, {
+ patchNum: element.patchSet,
+ fixSuggestions: [element.fixSuggestionInfo],
+ onCloseFixPreviewCallbacks: [],
+ });
+ });
+
+ test('apply-fix', async () => {
+ await loadDiff();
+
+ const setUrlSpy = sinon.stub(testResolver(navigationToken), 'setUrl');
+ stubRestApi('applyFixSuggestion').returns(
+ Promise.resolve({ok: true} as Response)
+ );
+
+ const button = queryAndAssert<HTMLElement>(element, 'gr-button.applyFix');
+ assert.isFalse(button.hasAttribute('disabled'));
+ button.click();
+
+ await waitUntil(() => setUrlSpy.called);
+ assert.equal(
+ setUrlSpy.lastCall.args[0],
+ '/c/test-repo/+/123/5..edit?forceReload=true'
+ );
+ });
+});
diff --git a/polygerrit-ui/app/elements/checks/gr-checks-results.ts b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
index 6cff5ab..b179e98 100644
--- a/polygerrit-ui/app/elements/checks/gr-checks-results.ts
+++ b/polygerrit-ui/app/elements/checks/gr-checks-results.ts
@@ -48,6 +48,7 @@
secondaryLinks,
tooltipForLink,
computeIsExpandable,
+ rectifyFix,
} from '../../models/checks/checks-util';
import {assertIsDefined, assert, unique} from '../../utils/common-util';
import {modifierPressed, whenVisible} from '../../utils/dom-util';
@@ -79,6 +80,7 @@
import {when} from 'lit/directives/when.js';
import {DropdownItem} from '../shared/gr-dropdown-list/gr-dropdown-list';
import './gr-checks-attempt';
+import './gr-checks-fix-preview';
import {changeViewModelToken} from '../../models/views/change';
import {formStyles} from '../../styles/form-styles';
@@ -562,8 +564,11 @@
private renderActions() {
const actions = [...(this.result?.actions ?? [])];
- const fixAction = createFixAction(this, this.result);
- if (fixAction) actions.unshift(fixAction);
+ let fixAction: Action | undefined = undefined;
+ if (!this.isExpanded) {
+ fixAction = createFixAction(this, this.result);
+ if (fixAction) actions.unshift(fixAction);
+ }
if (actions.length === 0) return;
const overflowItems = actions.slice(2).map(action => {
return {...action, id: action.name};
@@ -657,6 +662,10 @@
.message {
padding: var(--spacing-m) 0;
}
+ gr-checks-fix-preview {
+ margin: var(--spacing-l) 0;
+ max-width: 800px;
+ }
`,
];
}
@@ -681,6 +690,7 @@
.content=${this.result.message ?? ''}
></gr-formatted-text>
</gr-endpoint-decorator>
+ ${this.renderFix()}
`;
}
@@ -738,6 +748,20 @@
);
}
+ private renderFix() {
+ const fixSuggestionInfo = rectifyFix(
+ this.result?.fixes?.[0],
+ this.result?.checkName
+ );
+ if (!fixSuggestionInfo) return;
+ return html`
+ <gr-checks-fix-preview
+ .fixSuggestionInfo=${fixSuggestionInfo}
+ .patchSet=${this.result?.patchset}
+ ></gr-checks-fix-preview>
+ `;
+ }
+
private renderLink(link?: Link, targetBlank = true) {
if (!link) return;
const text = link.tooltip ?? tooltipForLink(link.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 89c7fd7..16d3d3e 100644
--- a/polygerrit-ui/app/elements/checks/gr-diff-check-result.ts
+++ b/polygerrit-ui/app/elements/checks/gr-diff-check-result.ts
@@ -236,6 +236,7 @@
}
private renderShowFixButton() {
+ if (this.isExpanded) return nothing;
const action = createFixAction(this, this.result);
if (!action) return nothing;
return html`
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 5aa266c..248dd62 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
@@ -83,10 +83,6 @@
<gr-result-expanded hidecodepointers=""></gr-result-expanded>
<div class="actions">
<gr-checks-action
- id="show-fix"
- context="diff-fix"
- ></gr-checks-action>
- <gr-checks-action
id="please-fix"
context="diff-fix"
></gr-checks-action>
diff --git a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
index 977ac2e..7c224a9 100644
--- a/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
+++ b/polygerrit-ui/app/elements/diff/gr-apply-fix-dialog/gr-apply-fix-dialog.ts
@@ -43,7 +43,7 @@
import {changeModelToken} from '../../../models/change/change-model';
import {getFileExtension} from '../../../utils/file-util';
-export interface FilePreview {
+export interface DiffPreview {
filepath: string;
preview: DiffInfo;
}
@@ -80,7 +80,7 @@
currentFix?: FixSuggestionInfo;
@state()
- currentPreviews: FilePreview[] = [];
+ currentPreviews: DiffPreview[] = [];
@state()
fixSuggestions?: FixSuggestionInfo[];
@@ -233,7 +233,7 @@
return html`<div slot="main">${items}</div>`;
}
- private renderDiff(preview: FilePreview) {
+ private renderDiff(preview: DiffPreview) {
const diff = preview.preview;
if (!anyLineTooLong(diff)) {
this.syntaxLayer.process(diff);
diff --git a/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview.ts b/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview.ts
index e1d4f89..48328ea 100644
--- a/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview.ts
+++ b/polygerrit-ui/app/elements/shared/gr-suggestion-diff-preview/gr-suggestion-diff-preview.ts
@@ -22,7 +22,7 @@
import {FixSuggestionInfo, NumericChangeId} from '../../../api/rest-api';
import {changeModelToken} from '../../../models/change/change-model';
import {subscribe} from '../../lit/subscription-controller';
-import {FilePreview} from '../../diff/gr-apply-fix-dialog/gr-apply-fix-dialog';
+import {DiffPreview} from '../../diff/gr-apply-fix-dialog/gr-apply-fix-dialog';
import {userModelToken} from '../../../models/user/user-model';
import {createUserFixSuggestion} from '../../../utils/comment-util';
import {commentModelToken} from '../gr-comment-model/gr-comment-model';
@@ -87,7 +87,7 @@
changeNum?: NumericChangeId;
@state()
- preview?: FilePreview;
+ preview?: DiffPreview;
@state()
diffPrefs?: DiffPreferencesInfo;
diff --git a/polygerrit-ui/app/models/checks/checks-fakes.ts b/polygerrit-ui/app/models/checks/checks-fakes.ts
index 1890639..cafe510 100644
--- a/polygerrit-ui/app/models/checks/checks-fakes.ts
+++ b/polygerrit-ui/app/models/checks/checks-fakes.ts
@@ -90,7 +90,7 @@
checkName: 'FAKE Super Check',
startedTimestamp: new Date(new Date().getTime() - 5 * 60 * 1000),
finishedTimestamp: new Date(new Date().getTime() + 5 * 60 * 1000),
- patchset: 1,
+ patchset: 3,
labelName: 'Verified',
isSingleAttempt: true,
isLatestAttempt: true,
@@ -159,16 +159,16 @@
{
internalResultId: 'f1r2',
category: Category.ERROR,
- summary: 'Suspicious Date',
- message: 'That was a holiday, you know.',
+ summary: 'Test Size Checker',
+ message: 'The test seems to be of large size, not medium.',
codePointers: [
{
- path: '/COMMIT_MSG',
+ path: 'plugins/BUILD',
range: {
- start_line: 3,
- start_character: 0,
- end_line: 3,
- end_character: 0,
+ start_line: 186,
+ start_character: 12,
+ end_line: 186,
+ end_character: 18,
},
},
],
@@ -177,14 +177,14 @@
description: 'This is the way to do it.',
replacements: [
{
- path: 'BUILD',
+ path: 'plugins/BUILD',
range: {
- start_line: 1,
- start_character: 0,
- end_line: 1,
- end_character: 0,
+ start_line: 186,
+ start_character: 12,
+ end_line: 186,
+ end_character: 18,
},
- replacement: '# This is now fixed.\n',
+ replacement: 'large',
},
],
},
diff --git a/polygerrit-ui/app/models/checks/checks-util.ts b/polygerrit-ui/app/models/checks/checks-util.ts
index fc18968..51bfcd9 100644
--- a/polygerrit-ui/app/models/checks/checks-util.ts
+++ b/polygerrit-ui/app/models/checks/checks-util.ts
@@ -136,9 +136,9 @@
export function rectifyFix(
fix: Fix | undefined,
- checkName: string
+ checkName: string | undefined
): FixSuggestionInfo | undefined {
- if (!fix?.replacements) return undefined;
+ if (!fix?.replacements || !checkName) return undefined;
const replacements = fix.replacements
.map(rectifyReplacement)
.filter(isDefined);