Move revision/commit based loading from gr-change-view into model
Release-Notes: skip
Google-Bug-Id: b/247042673
Change-Id: Ifa6b0abc94389f51e9d308c699e47c7da4936c00
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 54752b0..b7851a6 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
@@ -126,6 +126,7 @@
@property({type: Object}) revision?: RevisionInfo | EditRevisionInfo;
+ // TODO: Just use `revision.commit` instead.
@property({type: Object}) commitInfo?: CommitInfoWithRequiredCommit;
@property({type: Object}) serverConfig?: ServerInfo;
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
index e4a2cec..a80c058 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.ts
@@ -73,8 +73,6 @@
ChangeId,
ChangeInfo,
CommentThread,
- CommitId,
- CommitInfo,
ConfigInfo,
DetailedLabelInfo,
EDIT,
@@ -341,45 +339,22 @@
// Private but used in tests.
@state()
- commitInfo?: CommitInfo;
-
- // Private but used in tests.
- @state()
changeNum?: NumericChangeId;
@state()
private editingCommitMessage = false;
@state()
- private latestCommitMessage: string | null = '';
+ private latestCommitMessage = '';
@state() basePatchNum: BasePatchSetNum = PARENT;
@state() patchNum?: RevisionPatchSetNum;
// TODO: Migrate usages to this.patchNum and this.basePatchnum.
- // Use patchRange getter/setter.
- private _patchRange?: ChangeViewPatchRange;
+ @state() patchRange?: ChangeViewPatchRange;
- // TODO: Migrate usages to this.patchNum and this.basePatchnum.
- // Private but used in tests.
- @state()
- get patchRange() {
- return this._patchRange;
- }
-
- // TODO: Migrate usages to this.patchNum and this.basePatchnum.
- set patchRange(patchRange: ChangeViewPatchRange | undefined) {
- if (this._patchRange === patchRange) return;
- const oldPatchRange = this._patchRange;
- this._patchRange = patchRange;
- this.patchNumChanged();
- this.requestUpdate('patchRange', oldPatchRange);
- }
-
- // Private but used in tests.
- @state()
- selectedRevision?: RevisionInfo | EditRevisionInfo;
+ @state() revision?: RevisionInfo | EditRevisionInfo;
/**
* <gr-change-actions> populates this via two-way data binding.
@@ -746,6 +721,20 @@
);
subscribe(
this,
+ () => this.getChangeModel().revision$,
+ revision => (this.revision = revision)
+ );
+ subscribe(
+ this,
+ () => this.getChangeModel().latestRevision$,
+ revision => {
+ this.latestCommitMessage = this.prepareCommitMsgForLinkify(
+ revision?.commit?.message ?? ''
+ );
+ }
+ );
+ subscribe(
+ this,
() => this.getUserModel().account$,
account => {
this.account = account;
@@ -1237,7 +1226,8 @@
}
private renderHeaderTitle() {
- const resolveWeblinks = this.commitInfo?.resolve_conflicts_web_links ?? [];
+ const resolveWeblinks =
+ this.revision?.commit?.resolve_conflicts_web_links ?? [];
return html` <div class="headerTitle">
<div class="changeStatuses">
${this.changeStatuses.map(
@@ -1345,7 +1335,7 @@
.account=${this.account}
.changeNum=${this.changeNum}
.changeStatus=${this.change?.status}
- .commitNum=${this.commitInfo?.commit}
+ .commitNum=${this.revision?.commit?.commit}
.commitMessage=${this.latestCommitMessage}
.editMode=${this.getEditMode()}
.privateByDefault=${this.projectConfig?.private_by_default}
@@ -1373,8 +1363,8 @@
.change=${this.change}
.revertedChange=${this.revertedChange}
.account=${this.account}
- .revision=${this.selectedRevision}
- .commitInfo=${this.commitInfo}
+ .revision=${this.revision}
+ .commitInfo=${this.revision?.commit}
.serverConfig=${this.serverConfig}
.parentIsCurrent=${this.isParentCurrent()}
.repoConfig=${this.projectConfig}
@@ -1414,7 +1404,7 @@
remove-zero-width-space=""
>
<gr-formatted-text
- .content=${this.latestCommitMessage ?? ''}
+ .content=${this.latestCommitMessage}
.markdown=${false}
></gr-formatted-text>
</gr-editable-content>
@@ -1424,10 +1414,7 @@
<gr-endpoint-decorator name="commit-container">
<gr-endpoint-param name="change" .value=${this.change}>
</gr-endpoint-param>
- <gr-endpoint-param
- name="revision"
- .value=${this.selectedRevision}
- >
+ <gr-endpoint-param name="revision" .value=${this.revision}>
</gr-endpoint-param>
</gr-endpoint-decorator>
</div>
@@ -1478,10 +1465,7 @@
<gr-endpoint-decorator name=${tabHeader}>
<gr-endpoint-param name="change" .value=${this.change}>
</gr-endpoint-param>
- <gr-endpoint-param
- name="revision"
- .value=${this.selectedRevision}
- >
+ <gr-endpoint-param name="revision" .value=${this.revision}>
</gr-endpoint-param>
</gr-endpoint-decorator>
</paper-tab>
@@ -1517,7 +1501,7 @@
.account=${this.account}
.change=${this.change}
.changeNum=${this.changeNum}
- .commitInfo=${this.commitInfo}
+ .commitInfo=${this.revision?.commit}
.changeUrl=${this.computeChangeUrl()}
.editMode=${this.getEditMode()}
.loggedIn=${this.loggedIn}
@@ -1612,7 +1596,7 @@
<gr-endpoint-decorator .name=${pluginTabContentEndpoint}>
<gr-endpoint-param name="change" .value=${this.change}>
</gr-endpoint-param>
- <gr-endpoint-param name="revision" .value=${this.selectedRevision}></gr-endpoint-param>
+ <gr-endpoint-param name="revision" .value=${this.revision}></gr-endpoint-param>
</gr-endpoint-param>
</gr-endpoint-decorator>
`;
@@ -1623,7 +1607,7 @@
<gr-endpoint-decorator name="change-view-integration">
<gr-endpoint-param name="change" .value=${this.change}>
</gr-endpoint-param>
- <gr-endpoint-param name="revision" .value=${this.selectedRevision}>
+ <gr-endpoint-param name="revision" .value=${this.revision}>
</gr-endpoint-param>
</gr-endpoint-decorator>
@@ -1738,7 +1722,8 @@
}
private handleContentChanged(e: ValueChangedEvent) {
- this.latestCommitMessage = e.detail.value;
+ // optimistic update
+ this.latestCommitMessage = e.detail.value ?? '';
}
// Private but used in tests.
@@ -1765,7 +1750,6 @@
return;
}
- this.latestCommitMessage = this.prepareCommitMsgForLinkify(message);
this.editingCommitMessage = false;
fireReload(this, true);
})
@@ -2058,7 +2042,7 @@
const patchKnown =
!this.patchRange.patchNum ||
(this.allPatchSets ?? []).some(
- ps => ps.num === this.patchRange!.patchNum
+ ps => ps.num === this.patchRange?.patchNum
);
// _allPatchsets does not know value.patchNum so force a reload.
const forceReload = this.viewState.forceReload || !patchKnown;
@@ -2077,7 +2061,6 @@
// existing diff is not requested. See Issue 125270 for more details.
this.fileList?.resetFileState();
this.fileList?.collapseAllDiffs();
- this.reloadPatchNumDependentResources();
}
// If there is no change in patchset or changeNum, such as when user goes
@@ -2108,8 +2091,8 @@
// 2. We have to somehow trigger the change-model reloading. Otherwise
// this.change is not updated.
if (this.changeNum) {
- if (!this._patchRange?.patchNum) {
- this._patchRange = {
+ if (!this.patchRange?.patchNum) {
+ this.patchRange = {
basePatchNum: PARENT,
patchNum: computeLatestPatchNum(this.allPatchSets),
};
@@ -2531,6 +2514,8 @@
// TODO(wyatta) switch linkify sequence, see issue 5526.
// This is a zero-with space. It is added to prevent the linkify library
// from including R= or CC= as part of the email address.
+ // TODO: Is this comment referring to the ba-linkify library that we are
+ // not using anymore? If so, then remove this hack.
return msg.replace(REVIEWERS_REGEX, '$1=\u200B');
}
@@ -2582,9 +2567,6 @@
// one location.
if (!this.viewModelPatchNum && latestPsNum === editParentRev._number) {
this.patchRange = {...this.patchRange, patchNum: EDIT};
- // The file list is not reactive (yet) with regards to patch range
- // changes, so we have to actively trigger it.
- this.reloadPatchNumDependentResources();
}
}
@@ -2651,7 +2633,7 @@
this.prefs = await prefCompletes;
- if (!this.change) return false;
+ if (!this.change) return;
this.processEdit(this.change);
// Issue 4190: Coalesce missing topics to null.
@@ -2663,45 +2645,8 @@
if (!this.change.reviewer_updates) {
this.change.reviewer_updates = null as unknown as undefined;
}
- const latestRevisionSha = this.getLatestRevisionSHA(this.change);
- if (!latestRevisionSha)
- throw new Error('Could not find latest Revision Sha');
- const currentRevision = this.change.revisions[latestRevisionSha];
- if (currentRevision.commit && currentRevision.commit.message) {
- this.latestCommitMessage = this.prepareCommitMsgForLinkify(
- currentRevision.commit.message
- );
- } else {
- this.latestCommitMessage = null;
- }
this.computeRevertSubmitted(this.change);
- if (
- !this.patchRange ||
- !this.patchRange.patchNum ||
- this.patchRange.patchNum === currentRevision._number
- ) {
- // CommitInfo.commit is optional, and may need patching.
- if (currentRevision.commit && !currentRevision.commit.commit) {
- currentRevision.commit.commit = latestRevisionSha as CommitId;
- }
- this.commitInfo = currentRevision.commit;
- this.selectedRevision = currentRevision;
- // TODO: Fetch and process files.
- } else {
- if (!this.change?.revisions || !this.patchRange) return false;
- this.selectedRevision = Object.values(this.change.revisions).find(
- revision => {
- // edit patchset is a special one
- const thePatchNum = this.patchRange!.patchNum;
- if (thePatchNum === EDIT) {
- return revision._number === thePatchNum;
- }
- return revision._number === Number(`${thePatchNum}`);
- }
- );
- }
- return true;
}
private isParentCurrent() {
@@ -2713,49 +2658,6 @@
}
}
- // Private but used in tests.
- getLatestCommitMessage() {
- assertIsDefined(this.changeNum, 'changeNum');
- const lastpatchNum = computeLatestPatchNum(this.allPatchSets);
- if (lastpatchNum === undefined)
- throw new Error('missing lastPatchNum property');
- return this.restApiService
- .getChangeCommitInfo(this.changeNum, lastpatchNum)
- .then(commitInfo => {
- if (!commitInfo) return;
- this.latestCommitMessage = this.prepareCommitMsgForLinkify(
- commitInfo.message
- );
- });
- }
-
- // Private but used in tests.
- getLatestRevisionSHA(change: ChangeInfo | ParsedChangeInfo) {
- if (change.current_revision) return change.current_revision;
- // current_revision may not be present in the case where the latest rev is
- // a draft and the user doesn’t have permission to view that rev.
- let latestRev = null;
- let latestPatchNum = -1 as PatchSetNum;
- for (const [rev, revInfo] of Object.entries(change.revisions ?? {})) {
- if (revInfo._number > latestPatchNum) {
- latestRev = rev;
- latestPatchNum = revInfo._number;
- }
- }
- return latestRev;
- }
-
- // visible for testing
- loadAndSetCommitInfo() {
- assertIsDefined(this.changeNum, 'changeNum');
- assertIsDefined(this.patchRange?.patchNum, 'patchRange.patchNum');
- return this.restApiService
- .getChangeCommitInfo(this.changeNum, this.patchRange.patchNum)
- .then(commitInfo => {
- this.commitInfo = commitInfo;
- });
- }
-
/**
* Reload the change.
*
@@ -2794,30 +2696,7 @@
this.performPostChangeLoadTasks();
});
- let coreDataPromise;
-
- // If the patch number is specified
- if (this.patchRange && this.patchRange.patchNum) {
- // Because a specific patchset is specified, reload the resources that
- // are keyed by patch number or patch range.
- const patchResourcesLoaded = this.reloadPatchNumDependentResources();
- allDataPromises.push(patchResourcesLoaded);
-
- // Promise resolves when the change detail and patch dependent resources
- // have loaded.
- coreDataPromise = Promise.all([patchResourcesLoaded, loadingFlagSet]);
- } else {
- const latestCommitMessageLoaded = loadingFlagSet.then(() => {
- // If the latest commit message is known, there is nothing to do.
- if (this.latestCommitMessage) {
- return Promise.resolve();
- }
- return this.getLatestCommitMessage();
- });
- allDataPromises.push(latestCommitMessageLoaded);
-
- coreDataPromise = loadingFlagSet;
- }
+ const coreDataPromise = loadingFlagSet;
const mergeabilityLoaded = coreDataPromise.then(() =>
this.getMergeability()
);
@@ -2890,14 +2769,6 @@
);
}
- /**
- * Kicks off requests for resources that rely on the patch range
- * (`this.patchRange`) being defined.
- */
- reloadPatchNumDependentResources() {
- return this.loadAndSetCommitInfo();
- }
-
// Private but used in tests
getMergeability(): Promise<void> {
if (!this.change) {
@@ -2948,13 +2819,11 @@
);
}
- private computeCommitCollapsible() {
- if (!this.latestCommitMessage) {
- return false;
- }
+ private computeCommitCollapsible(): boolean {
return (
+ !!this.latestCommitMessage &&
this.latestCommitMessage.split('\n').length >=
- MIN_LINES_FOR_COMMIT_COLLAPSE
+ MIN_LINES_FOR_COMMIT_COLLAPSE
);
}
@@ -3081,21 +2950,6 @@
}
}
- private patchNumChanged() {
- if (!this.selectedRevision || !this.patchRange?.patchNum) {
- return;
- }
- assertIsDefined(this.change, 'change');
-
- if (this.patchRange.patchNum === this.selectedRevision._number) {
- return;
- }
- if (!this.change.revisions) return;
- this.selectedRevision = Object.values(this.change.revisions).find(
- revision => revision._number === this.patchRange!.patchNum
- );
- }
-
/**
* If an edit exists already, load it. Otherwise, toggle edit mode via the
* navigation API.
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
index df48192..44a8428 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.ts
@@ -43,7 +43,6 @@
createUserConfig,
TEST_NUMERIC_CHANGE_ID,
TEST_PROJECT_NAME,
- createEditRevision,
createAccountWithIdNameAndEmail,
createChangeViewChange,
createRelatedChangeAndCommitInfo,
@@ -56,14 +55,12 @@
ApprovalInfo,
BasePatchSetNum,
ChangeId,
- ChangeInfo,
CommitId,
EDIT,
NumericChangeId,
PARENT,
RelatedChangeAndCommitInfo,
ReviewInputTag,
- RevisionInfo,
RevisionPatchSetNum,
RobotId,
RobotCommentInfo,
@@ -1466,9 +1463,6 @@
const reloadStub = sinon
.stub(element, 'loadData')
.callsFake(() => Promise.resolve());
- const reloadPatchDependentStub = sinon
- .stub(element, 'reloadPatchNumDependentResources')
- .callsFake(() => Promise.resolve());
assertIsDefined(element.fileList);
await element.fileList.updateComplete;
const collapseStub = sinon.stub(element.fileList, 'collapseAllDiffs');
@@ -1499,7 +1493,6 @@
await waitEventLoop();
assert.equal(element.fileList.selectedIndex, 0);
assert.isFalse(reloadStub.calledTwice);
- assert.isTrue(reloadPatchDependentStub.calledOnce);
assert.isTrue(collapseStub.calledTwice);
});
@@ -1580,26 +1573,6 @@
);
});
- test('get latest revision', () => {
- let change: ChangeInfo = {
- ...createChange(),
- revisions: {
- rev1: createRevision(1),
- rev3: createRevision(3),
- },
- current_revision: 'rev3' as CommitId,
- };
- assert.equal(element.getLatestRevisionSHA(change), 'rev3');
- change = {
- ...createChange(),
- revisions: {
- rev1: createRevision(1),
- },
- current_revision: undefined,
- };
- assert.equal(element.getLatestRevisionSHA(change), 'rev1');
- });
-
test('show commit message edit button', () => {
const change = createParsedChange();
const mergedChanged: ParsedChangeInfo = {
@@ -1658,21 +1631,6 @@
assert.isNull(element.change!.topic);
});
- test('commit sha is populated from getChangeDetail', async () => {
- changeModel.setState({
- loadingStatus: LoadingStatus.LOADED,
- change: {
- ...createChangeViewChange(),
- labels: {},
- current_revision: 'foo' as CommitId,
- revisions: {foo: createRevision()},
- },
- });
-
- await element.performPostChangeLoadTasks();
- assert.equal('foo', element.commitInfo!.commit);
- });
-
test('getBasePatchNum', async () => {
element.change = {
...createChangeViewChange(),
@@ -1992,58 +1950,6 @@
assert.isTrue(setUrlStub.called);
});
- test('selectedRevision updates when patchNum is changed', async () => {
- const revision1: RevisionInfo = createRevision(1);
- const revision2: RevisionInfo = createRevision(2);
- changeModel.setState({
- loadingStatus: LoadingStatus.LOADED,
- change: {
- ...createChangeViewChange(),
- revisions: {
- aaa: revision1,
- bbb: revision2,
- },
- labels: {},
- actions: {},
- current_revision: 'bbb' as CommitId,
- },
- });
- userModel.setPreferences(createPreferences());
-
- element.patchRange = {patchNum: 2 as RevisionPatchSetNum};
- await element.performPostChangeLoadTasks();
- assert.strictEqual(element.selectedRevision, revision2);
-
- element.patchRange = {patchNum: 1 as RevisionPatchSetNum};
- await element.updateComplete;
- assert.strictEqual(element.selectedRevision, revision1);
- });
-
- test('selectedRevision is assigned when patchNum is edit', async () => {
- const revision1 = createRevision(1);
- const revision2 = createRevision(2);
- const revision3 = createEditRevision();
- changeModel.setState({
- loadingStatus: LoadingStatus.LOADED,
- change: {
- ...createChangeViewChange(),
- revisions: {
- aaa: revision1,
- bbb: revision2,
- ccc: revision3,
- },
- labels: {},
- actions: {},
- current_revision: 'ccc' as CommitId,
- },
- });
- stubRestApi('getPreferences').returns(Promise.resolve(createPreferences()));
-
- element.patchRange = {patchNum: EDIT};
- await element.performPostChangeLoadTasks();
- assert.strictEqual(element.selectedRevision, revision3);
- });
-
test('sendShowChangeEvent', () => {
const change = {...createChangeViewChange(), labels: {}};
element.change = {...change};
@@ -2179,7 +2085,7 @@
suite('plugin endpoints', () => {
test('endpoint params', async () => {
element.change = {...createChangeViewChange(), labels: {}};
- element.selectedRevision = createRevision();
+ element.revision = createRevision();
const promise = mockPromise();
window.Gerrit.install(
promise.resolve,
@@ -2193,7 +2099,7 @@
.getLastAttached();
assert.strictEqual((hookEl as any).plugin, plugin);
assert.strictEqual((hookEl as any).change, element.change);
- assert.strictEqual((hookEl as any).revision, element.selectedRevision);
+ assert.strictEqual((hookEl as any).revision, element.revision);
});
});
@@ -2255,14 +2161,8 @@
basePatchNum: PARENT,
patchNum: 1 as RevisionPatchSetNum,
};
- sinon
- .stub(element, 'performPostChangeLoadTasks')
- .returns(Promise.resolve(false));
+ sinon.stub(element, 'performPostChangeLoadTasks');
sinon.stub(element, 'getMergeability').returns(Promise.resolve());
- sinon.stub(element, 'getLatestCommitMessage').returns(Promise.resolve());
- sinon
- .stub(element, 'reloadPatchNumDependentResources')
- .returns(Promise.resolve());
});
test("don't report changeDisplayed on reply", async () => {
diff --git a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.ts b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.ts
index e1dfd1f..3082979 100644
--- a/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.ts
+++ b/polygerrit-ui/app/elements/diff/gr-patch-range-select/gr-patch-range-select.ts
@@ -14,7 +14,6 @@
getParentIndex,
getRevisionByPatchNum,
isMergeParent,
- sortRevisions,
PatchSet,
convertToPatchSetNum,
} from '../../../utils/patch-set-util';
@@ -159,7 +158,7 @@
subscribe(
this,
() => this.getChangeModel().revisions$,
- x => (this.sortedRevisions = sortRevisions(Object.values(x || {})))
+ x => (this.sortedRevisions = x)
);
subscribe(
this,
diff --git a/polygerrit-ui/app/models/change/change-model.ts b/polygerrit-ui/app/models/change/change-model.ts
index b6b9de7..6d575ce 100644
--- a/polygerrit-ui/app/models/change/change-model.ts
+++ b/polygerrit-ui/app/models/change/change-model.ts
@@ -13,6 +13,7 @@
PreferencesInfo,
RevisionPatchSetNum,
PatchSetNumber,
+ CommitId,
} from '../../types/common';
import {DefaultBase} from '../../constants/constants';
import {combineLatest, from, fromEvent, Observable, forkJoin, of} from 'rxjs';
@@ -27,6 +28,7 @@
computeAllPatchSets,
computeLatestPatchNum,
computeLatestPatchNumWithEdit,
+ sortRevisions,
} from '../../utils/patch-set-util';
import {ParsedChangeInfo} from '../../types/types';
import {fireAlert} from '../../utils/event-util';
@@ -72,6 +74,32 @@
}
/**
+ * `change.revisions` is a dictionary mapping the revision sha to RevisionInfo,
+ * but the info object itself does not contain the sha, which is a problem when
+ * working with just the info objects.
+ *
+ * So we are iterating over the map here and are assigning the sha map key to
+ * the property `revision.commit.commit`.
+ *
+ * As usual we are treating data objects as immutable, so we are doind a lot of
+ * cloning here.
+ */
+export function updateRevisionsWithCommitShas(changeInput?: ParsedChangeInfo) {
+ if (!changeInput?.revisions) return changeInput;
+ const changeOutput = {...changeInput, revisions: {...changeInput.revisions}};
+ for (const sha of Object.keys(changeOutput.revisions)) {
+ const revision = changeOutput.revisions[sha];
+ if (revision?.commit && !revision.commit.commit) {
+ changeOutput.revisions[sha] = {
+ ...revision,
+ commit: {...revision.commit, commit: sha as CommitId},
+ };
+ }
+ }
+ return changeOutput;
+}
+
+/**
* Updates the change object with information from the saved `edit` patchset.
*/
// visible for testing
@@ -179,9 +207,8 @@
public readonly labels$ = select(this.change$, change => change?.labels);
- public readonly revisions$ = select(
- this.change$,
- change => change?.revisions
+ public readonly revisions$ = select(this.change$, change =>
+ sortRevisions(Object.values(change?.revisions || {}))
);
public readonly patchsets$ = select(this.change$, change =>
@@ -264,6 +291,24 @@
computeBase(viewModelBasePatchNum, patchNum, change, preferences)
);
+ private selectRevision(
+ revisionNum$: Observable<RevisionPatchSetNum | undefined>
+ ) {
+ return select(
+ combineLatest([this.revisions$, revisionNum$]),
+ ([revisions, patchNum]) => {
+ if (!revisions || !patchNum) return undefined;
+ return Object.values(revisions).find(
+ revision => revision._number === patchNum
+ );
+ }
+ );
+ }
+
+ public readonly revision$ = this.selectRevision(this.patchNum$);
+
+ public readonly latestRevision$ = this.selectRevision(this.latestPatchNum$);
+
public readonly isOwner$: Observable<boolean> = select(
combineLatest([this.change$, this.userModel.account$]),
([change, account]) => isOwner(change, account)
@@ -295,7 +340,8 @@
withLatestFrom(this.viewModel.patchNum$),
map(([[change, edit], patchNum]) =>
updateChangeWithEdit(change, edit, patchNum)
- )
+ ),
+ map(updateRevisionsWithCommitShas)
)
.subscribe(change => {
// The change service is currently a singleton, so we have to be
diff --git a/polygerrit-ui/app/models/change/change-model_test.ts b/polygerrit-ui/app/models/change/change-model_test.ts
index c11c15b..2ec0b20 100644
--- a/polygerrit-ui/app/models/change/change-model_test.ts
+++ b/polygerrit-ui/app/models/change/change-model_test.ts
@@ -29,14 +29,28 @@
} from '../../types/common';
import {ParsedChangeInfo} from '../../types/types';
import {getAppContext} from '../../services/app-context';
-import {ChangeState, LoadingStatus, updateChangeWithEdit} from './change-model';
+import {
+ ChangeState,
+ LoadingStatus,
+ updateChangeWithEdit,
+ updateRevisionsWithCommitShas,
+} from './change-model';
import {ChangeModel} from './change-model';
import {assert} from '@open-wc/testing';
import {testResolver} from '../../test/common-test-setup';
import {userModelToken} from '../user/user-model';
-import {changeViewModelToken} from '../views/change';
+import {ChangeViewModel, changeViewModelToken} from '../views/change';
import {navigationToken} from '../../elements/core/gr-navigation/gr-navigation';
+suite('updateRevisionsWithCommitShas() tests', () => {
+ test('undefined edit', async () => {
+ const change = createParsedChange();
+ const updated = updateRevisionsWithCommitShas(change);
+ assert.equal(change?.revisions?.['abc'].commit?.commit, undefined);
+ assert.equal(updated?.revisions?.['abc'].commit?.commit, 'abc' as CommitId);
+ });
+});
+
suite('updateChangeWithEdit() tests', () => {
test('undefined change', async () => {
assert.isUndefined(updateChangeWithEdit());
@@ -69,6 +83,7 @@
});
suite('change model tests', () => {
+ let changeViewModel: ChangeViewModel;
let changeModel: ChangeModel;
let knownChange: ParsedChangeInfo;
const testCompleted = new Subject<void>();
@@ -84,25 +99,18 @@
}
setup(() => {
+ changeViewModel = testResolver(changeViewModelToken);
changeModel = new ChangeModel(
testResolver(navigationToken),
- testResolver(changeViewModelToken),
+ changeViewModel,
getAppContext().restApiService,
testResolver(userModelToken)
);
knownChange = {
...createChange(),
revisions: {
- sha1: {
- ...createRevision(1),
- description: 'patch 1',
- _number: 1 as PatchSetNumber,
- },
- sha2: {
- ...createRevision(2),
- description: 'patch 2',
- _number: 2 as PatchSetNumber,
- },
+ sha1: {...createRevision(1), description: 'patch 1'},
+ sha2: {...createRevision(2), description: 'patch 2'},
},
status: ChangeStatus.NEW,
current_revision: 'abc' as CommitId,
@@ -132,7 +140,7 @@
promise.resolve(knownChange);
state = await waitForLoadingStatus(LoadingStatus.LOADED);
assert.equal(stub.callCount, 1);
- assert.equal(state?.change, knownChange);
+ assert.deepEqual(state?.change, updateRevisionsWithCommitShas(knownChange));
});
test('reload a change', async () => {
@@ -148,12 +156,12 @@
document.dispatchEvent(new CustomEvent('reload'));
state = await waitForLoadingStatus(LoadingStatus.RELOADING);
assert.equal(stub.callCount, 2);
- assert.equal(state?.change, knownChange);
+ assert.deepEqual(state?.change, updateRevisionsWithCommitShas(knownChange));
promise.resolve(knownChange);
state = await waitForLoadingStatus(LoadingStatus.LOADED);
assert.equal(stub.callCount, 2);
- assert.equal(state?.change, knownChange);
+ assert.deepEqual(state?.change, updateRevisionsWithCommitShas(knownChange));
});
test('navigating to another change', async () => {
@@ -183,7 +191,7 @@
promise.resolve(otherChange);
state = await waitForLoadingStatus(LoadingStatus.LOADED);
assert.equal(stub.callCount, 2);
- assert.equal(state?.change, otherChange);
+ assert.deepEqual(state?.change, updateRevisionsWithCommitShas(otherChange));
});
test('navigating to dashboard', async () => {
@@ -211,7 +219,7 @@
testResolver(changeViewModelToken).setState(createChangeViewState());
state = await waitForLoadingStatus(LoadingStatus.LOADED);
assert.equal(stub.callCount, 3);
- assert.equal(state?.change, knownChange);
+ assert.deepEqual(state?.change, updateRevisionsWithCommitShas(knownChange));
});
test('changeModel.fetchChangeUpdates on latest', async () => {
@@ -294,4 +302,28 @@
changeModel.updateStateChange(createParsedChange());
assert.equal(spy.callCount, 2);
});
+
+ test('revision$ selector latest', async () => {
+ changeViewModel.updateState({patchNum: undefined});
+ changeModel.updateState({change: knownChange});
+ await waitUntilObserved(changeModel.revision$, x => x?._number === 2);
+ });
+
+ test('revision$ selector 1', async () => {
+ changeViewModel.updateState({patchNum: 1 as PatchSetNumber});
+ changeModel.updateState({change: knownChange});
+ await waitUntilObserved(changeModel.revision$, x => x?._number === 1);
+ });
+
+ test('latestRevision$ selector latest', async () => {
+ changeViewModel.updateState({patchNum: undefined});
+ changeModel.updateState({change: knownChange});
+ await waitUntilObserved(changeModel.latestRevision$, x => x?._number === 2);
+ });
+
+ test('latestRevision$ selector 1', async () => {
+ changeViewModel.updateState({patchNum: 1 as PatchSetNumber});
+ changeModel.updateState({change: knownChange});
+ await waitUntilObserved(changeModel.latestRevision$, x => x?._number === 2);
+ });
});