Fix/issue 337524 multidiff order (#337565)

* Fix SCM multi-diff file ordering

* test: cover SCM multi-diff file ordering

* test: cover SCM multi-diff file ordering

* test: simplify SCM multi-diff ordering coverage

* test: organize SCM multi-diff test imports

* test: fix SCM multi-diff review findings
This commit is contained in:
Ürem Sancaktutan authored and GitHub committed 2026-10-05 10:38:28 +00:00
1 parent 45a59e27c2
commit 1e72bb9dd2
2 files changed
+75 -4

No files matched your search

@@ -7,6 +7,7 @@ import { ValueWithChangeEvent } from '../../../../base/common/event.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { observableFromEvent, ValueWithChangeEventFromObservable, waitForState } from '../../../../base/common/observable.js';
import { basename } from '../../../../base/common/path.js';
import { comparePaths } from '../../../../base/common/comparers.js';
import { URI, UriComponents } from '../../../../base/common/uri.js';
import { IMultiDiffEditorOptions } from '../../../../editor/common/multiDiffEditor.js';
import { localize2 } from '../../../../nls.js';
@@ -176,7 +177,9 @@ class ScmResolvedMultiDiffSource implements IResolvedMultiDiffSource {
) {
this._resources = observableFromEvent<MultiDiffEditorItem[]>(
this._group.onDidChangeResources,
() => /** @description resources */ this._group.resources.map(e => new MultiDiffEditorItem(e.multiDiffEditorOriginalUri, e.multiDiffEditorModifiedUri, e.sourceUri))
() => /** @description resources */[...this._group.resources]
.sort((a, b) => comparePaths(a.sourceUri.fsPath, b.sourceUri.fsPath))
.map(e => new MultiDiffEditorItem(e.multiDiffEditorOriginalUri, e.multiDiffEditorModifiedUri, e.sourceUri))
);
this.resources = new ValueWithChangeEventFromObservable(this._resources);
this.contextKeys = {
@@ -4,14 +4,82 @@
*--------------------------------------------------------------------------------------------*/
import assert from 'assert';
import { Emitter } from '../../../../../base/common/event.js';
import { Emitter, Event } from '../../../../../base/common/event.js';
import { observableValue } from '../../../../../base/common/observable.js';
import { URI } from '../../../../../base/common/uri.js';
import { mock } from '../../../../../base/test/common/mock.js';
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
import { IActivityService } from '../../../../services/activity/common/activity.js';
import { ISCMHistoryProvider } from '../../../scm/common/history.js';
import { ISCMProvider, ISCMRepository, ISCMService } from '../../../scm/common/scm.js';
import { ScmHistoryItemResolver } from '../../browser/scmMultiDiffSourceResolver.js';
import { ISCMProvider, ISCMRepository, ISCMResource, ISCMResourceGroup, ISCMService } from '../../../scm/common/scm.js';
import { ScmMultiDiffSourceResolver, ScmHistoryItemResolver } from '../../browser/scmMultiDiffSourceResolver.js';
suite('ScmMultiDiffSourceResolver', () => {
const disposables = ensureNoDisposablesAreLeakedInTestSuite();
test('sorts resources by path', async () => {
const resources: ISCMResource[] = [];
const onDidChangeResources = disposables.add(new Emitter<void>());
const group = new class extends mock<ISCMResourceGroup>() {
override readonly id = 'changes';
override readonly label = 'Changes';
override readonly resources = resources;
override readonly onDidChangeResources = onDidChangeResources.event;
}();
const provider = new class extends mock<ISCMProvider>() {
override readonly id = 'scm0';
override readonly providerId = 'scm0';
override readonly rootUri = URI.file('/repository');
override readonly groups = [group];
override readonly onDidChangeResourceGroups = new Emitter<void>().event;
}();
const repository = new class extends mock<ISCMRepository>() {
override readonly id = provider.id;
override readonly provider = provider;
}();
const repositories = [repository];
const onDidAddRepository = disposables.add(new Emitter<ISCMRepository>());
const scmService = new class extends mock<ISCMService>() {
override readonly onDidAddRepository = onDidAddRepository.event;
override get repositories(): Iterable<ISCMRepository> { return repositories; }
override get repositoryCount(): number { return repositories.length; }
override getRepository(idOrResource: string | URI): ISCMRepository | undefined {
return typeof idOrResource === 'string' ? repositories.find(r => r.id === idOrResource) : undefined;
}
}();
const activityService = new class extends mock<IActivityService>() {
override readonly onDidChangeActivity = Event.None;
override getViewContainerActivities() { return []; }
}();
const resolver = new ScmMultiDiffSourceResolver(scmService, activityService);
const sourceUri = ScmMultiDiffSourceResolver.getMultiDiffSourceUri(provider.rootUri.toString(), group.id);
const sourcePromise = resolver.resolveDiffSource(sourceUri);
const createResource = (path: string) => new class extends mock<ISCMResource>() {
override readonly sourceUri = URI.file(path);
override readonly multiDiffEditorModifiedUri = this.sourceUri;
}();
resources.push(
createResource('/repository/githubServer.ts'),
createResource('/repository/env.ts'),
createResource('/repository/githubUri.ts'),
);
onDidChangeResources.fire();
const source = await sourcePromise;
assert.deepStrictEqual(
source.resources.value.map(resource => resource.goToFileUri?.path),
[
'/repository/env.ts',
'/repository/githubServer.ts',
'/repository/githubUri.ts',
]
);
});
});
suite('ScmHistoryItemResolver', () => {