Path: blob/main/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { generateUuid } from '../../../../../base/common/uuid.js';7import { ExtensionsListView } from '../../browser/extensionsViews.js';8import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';9import { IExtensionsWorkbenchService } from '../../common/extensions.js';10import { ExtensionsWorkbenchService } from '../../browser/extensionsWorkbenchService.js';11import {12IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions,13getTargetPlatform, SortBy14} from '../../../../../platform/extensionManagement/common/extensionManagement.js';15import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IProfileAwareExtensionManagementService, IWorkbenchExtensionManagementService } from '../../../../services/extensionManagement/common/extensionManagement.js';16import { IExtensionRecommendationsService, ExtensionRecommendationReason } from '../../../../services/extensionRecommendations/common/extensionRecommendations.js';17import { getGalleryExtensionId } from '../../../../../platform/extensionManagement/common/extensionManagementUtil.js';18import { TestExtensionEnablementService } from '../../../../services/extensionManagement/test/browser/extensionEnablementService.test.js';19import { ExtensionGalleryService } from '../../../../../platform/extensionManagement/common/extensionGalleryService.js';20import { IURLService } from '../../../../../platform/url/common/url.js';21import { Event } from '../../../../../base/common/event.js';22import { IPager } from '../../../../../base/common/paging.js';23import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js';24import { NullTelemetryService } from '../../../../../platform/telemetry/common/telemetryUtils.js';25import { IExtensionService, toExtensionDescription } from '../../../../services/extensions/common/extensions.js';26import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js';27import { TestMenuService } from '../../../../test/browser/workbenchTestServices.js';28import { TestSharedProcessService } from '../../../../test/electron-browser/workbenchTestServices.js';29import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js';30import { ILogService, NullLogService } from '../../../../../platform/log/common/log.js';31import { NativeURLService } from '../../../../../platform/url/common/urlService.js';32import { URI } from '../../../../../base/common/uri.js';33import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';34import { SinonStub } from 'sinon';35import { IRemoteAgentService } from '../../../../services/remote/common/remoteAgentService.js';36import { RemoteAgentService } from '../../../../services/remote/electron-browser/remoteAgentService.js';37import { ExtensionType, IExtension } from '../../../../../platform/extensions/common/extensions.js';38import { ISharedProcessService } from '../../../../../platform/ipc/electron-browser/services.js';39import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js';40import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js';41import { IMenuService } from '../../../../../platform/actions/common/actions.js';42import { TestContextService } from '../../../../test/common/workbenchTestServices.js';43import { IViewDescriptorService, ViewContainerLocation } from '../../../../common/views.js';44import { Schemas } from '../../../../../base/common/network.js';45import { platform } from '../../../../../base/common/platform.js';46import { arch } from '../../../../../base/common/process.js';47import { IProductService } from '../../../../../platform/product/common/productService.js';48import { CancellationToken } from '../../../../../base/common/cancellation.js';49import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';50import { IUpdateService, State } from '../../../../../platform/update/common/update.js';51import { IFileService } from '../../../../../platform/files/common/files.js';52import { FileService } from '../../../../../platform/files/common/fileService.js';53import { IUserDataProfileService } from '../../../../services/userDataProfile/common/userDataProfile.js';54import { UserDataProfileService } from '../../../../services/userDataProfile/common/userDataProfileService.js';55import { toUserDataProfile } from '../../../../../platform/userDataProfile/common/userDataProfile.js';5657suite('ExtensionsViews Tests', () => {5859const disposableStore = ensureNoDisposablesAreLeakedInTestSuite();6061let instantiationService: TestInstantiationService;62let testableView: ExtensionsListView;6364const localEnabledTheme = aLocalExtension('first-enabled-extension', { categories: ['Themes', 'random'] }, { installedTimestamp: 123456 });65const localEnabledLanguage = aLocalExtension('second-enabled-extension', { categories: ['Programming languages'], version: '1.0.0' }, { installedTimestamp: Date.now(), updated: false });66const localDisabledTheme = aLocalExtension('first-disabled-extension', { categories: ['themes'] }, { installedTimestamp: 234567 });67const localDisabledLanguage = aLocalExtension('second-disabled-extension', { categories: ['programming languages'] }, { installedTimestamp: Date.now() - 50000, updated: true });68const localRandom = aLocalExtension('random-enabled-extension', { categories: ['random'] }, { installedTimestamp: 345678 });69const builtInTheme = aLocalExtension('my-theme', { categories: ['Themes'], contributes: { themes: ['my-theme'] } }, { type: ExtensionType.System, installedTimestamp: 222 });70const builtInBasic = aLocalExtension('my-lang', { categories: ['Programming Languages'], contributes: { grammars: [{ language: 'my-language' }] } }, { type: ExtensionType.System, installedTimestamp: 666666 });7172let queryPage = aPage([]);73const galleryExtensions: IGalleryExtension[] = [];7475const workspaceRecommendationA = aGalleryExtension('workspace-recommendation-A');76const workspaceRecommendationB = aGalleryExtension('workspace-recommendation-B');77const configBasedRecommendationA = aGalleryExtension('configbased-recommendation-A');78const configBasedRecommendationB = aGalleryExtension('configbased-recommendation-B');79const fileBasedRecommendationA = aGalleryExtension('filebased-recommendation-A');80const fileBasedRecommendationB = aGalleryExtension('filebased-recommendation-B');81const otherRecommendationA = aGalleryExtension('other-recommendation-A');8283setup(async () => {84instantiationService = disposableStore.add(new TestInstantiationService());85instantiationService.stub(ITelemetryService, NullTelemetryService);86instantiationService.stub(ILogService, NullLogService);87instantiationService.stub(IFileService, disposableStore.add(new FileService(new NullLogService())));88instantiationService.stub(IProductService, {});8990instantiationService.stub(IWorkspaceContextService, new TestContextService());91instantiationService.stub(IConfigurationService, new TestConfigurationService());9293instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService);94instantiationService.stub(ISharedProcessService, TestSharedProcessService);9596instantiationService.stub(IWorkbenchExtensionManagementService, {97onInstallExtension: Event.None,98onDidInstallExtensions: Event.None,99onUninstallExtension: Event.None,100onDidUninstallExtension: Event.None,101onDidUpdateExtensionMetadata: Event.None,102onDidChangeProfile: Event.None,103onProfileAwareDidInstallExtensions: Event.None,104async getInstalled() { return []; },105async getInstalledWorkspaceExtensions() { return []; },106async canInstall() { return true; },107async getExtensionsControlManifest() { return { malicious: [], deprecated: {}, search: [], publisherMapping: {} }; },108async getTargetPlatform() { return getTargetPlatform(platform, arch); },109async updateMetadata(local) { return local; }110});111instantiationService.stub(IRemoteAgentService, RemoteAgentService);112instantiationService.stub(IContextKeyService, new MockContextKeyService());113instantiationService.stub(IMenuService, new TestMenuService());114115const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService, label: 'local', id: 'vscode-local' };116instantiationService.stub(IExtensionManagementServerService, {117get localExtensionManagementServer(): IExtensionManagementServer {118return localExtensionManagementServer;119},120getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null {121if (extension.location.scheme === Schemas.file) {122return localExtensionManagementServer;123}124throw new Error(`Invalid Extension ${extension.location}`);125}126});127128instantiationService.stub(IWorkbenchExtensionEnablementService, disposableStore.add(new TestExtensionEnablementService(instantiationService)));129instantiationService.stub(IUserDataProfileService, disposableStore.add(new UserDataProfileService(toUserDataProfile('test', 'test', URI.file('foo'), URI.file('cache')))));130131const reasons: { [key: string]: any } = {};132reasons[workspaceRecommendationA.identifier.id] = { reasonId: ExtensionRecommendationReason.Workspace };133reasons[workspaceRecommendationB.identifier.id] = { reasonId: ExtensionRecommendationReason.Workspace };134reasons[fileBasedRecommendationA.identifier.id] = { reasonId: ExtensionRecommendationReason.File };135reasons[fileBasedRecommendationB.identifier.id] = { reasonId: ExtensionRecommendationReason.File };136reasons[otherRecommendationA.identifier.id] = { reasonId: ExtensionRecommendationReason.Executable };137reasons[configBasedRecommendationA.identifier.id] = { reasonId: ExtensionRecommendationReason.WorkspaceConfig };138instantiationService.stub(IExtensionRecommendationsService, {139getWorkspaceRecommendations() {140return Promise.resolve([141workspaceRecommendationA.identifier.id,142workspaceRecommendationB.identifier.id]);143},144getConfigBasedRecommendations() {145return Promise.resolve({146important: [configBasedRecommendationA.identifier.id],147others: [configBasedRecommendationB.identifier.id],148});149},150getImportantRecommendations(): Promise<string[]> {151return Promise.resolve([]);152},153getFileBasedRecommendations() {154return [155fileBasedRecommendationA.identifier.id,156fileBasedRecommendationB.identifier.id157];158},159getOtherRecommendations() {160return Promise.resolve([161configBasedRecommendationB.identifier.id,162otherRecommendationA.identifier.id163]);164},165getAllRecommendationsWithReason() {166return reasons;167}168});169instantiationService.stub(IURLService, NativeURLService);170171instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localEnabledTheme, localEnabledLanguage, localRandom, localDisabledTheme, localDisabledLanguage, builtInTheme, builtInBasic]);172instantiationService.stubPromise(IExtensionManagementService, 'getExtensgetExtensionsControlManifestionsReport', {});173174instantiationService.stub(IExtensionGalleryService, <Partial<IExtensionGalleryService>>{175query: async () => {176return queryPage;177},178getCompatibleExtension: async (gallery) => {179return gallery;180},181getExtensions: async (infos) => {182const result: IGalleryExtension[] = [];183for (const info of infos) {184const extension = galleryExtensions.find(e => e.identifier.id === info.id);185if (extension) {186result.push(extension);187}188}189return result;190},191isEnabled: () => true,192isExtensionCompatible: async () => true,193});194195instantiationService.stub(IViewDescriptorService, {196getViewLocationById(): ViewContainerLocation {197return ViewContainerLocation.Sidebar;198},199onDidChangeLocation: Event.None200});201202instantiationService.stub(IExtensionService, {203onDidChangeExtensions: Event.None,204extensions: [205toExtensionDescription(localEnabledTheme),206toExtensionDescription(localEnabledLanguage),207toExtensionDescription(localRandom),208toExtensionDescription(builtInTheme),209toExtensionDescription(builtInBasic)210],211canAddExtension: (extension) => true,212whenInstalledExtensionsRegistered: () => Promise.resolve(true)213});214await (<TestExtensionEnablementService>instantiationService.get(IWorkbenchExtensionEnablementService)).setEnablement([localDisabledTheme], EnablementState.DisabledGlobally);215await (<TestExtensionEnablementService>instantiationService.get(IWorkbenchExtensionEnablementService)).setEnablement([localDisabledLanguage], EnablementState.DisabledGlobally);216217instantiationService.stub(IUpdateService, { onStateChange: Event.None, state: State.Uninitialized });218instantiationService.set(IExtensionsWorkbenchService, disposableStore.add(instantiationService.createInstance(ExtensionsWorkbenchService)));219testableView = disposableStore.add(instantiationService.createInstance(ExtensionsListView, {}, { id: '', title: '' }));220queryPage = aPage([]);221222galleryExtensions.splice(0, galleryExtensions.length, ...[223workspaceRecommendationA,224workspaceRecommendationB,225configBasedRecommendationA,226configBasedRecommendationB,227fileBasedRecommendationA,228fileBasedRecommendationB,229otherRecommendationA230]);231});232233test('Test query types', () => {234assert.strictEqual(ExtensionsListView.isBuiltInExtensionsQuery('@builtin'), true);235assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@installed'), true);236assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@enabled'), true);237assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@disabled'), true);238assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@outdated'), true);239assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@updates'), true);240assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@sort:name'), true);241assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@sort:updateDate'), true);242assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@installed searchText'), true);243assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@enabled searchText'), true);244assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@disabled searchText'), true);245assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@outdated searchText'), true);246assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@updates searchText'), true);247});248249test('Test empty query equates to sort by install count', async () => {250const target = <SinonStub>instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage());251await testableView.show('');252assert.ok(target.calledOnce);253const options: IQueryOptions = target.args[0][0];254assert.strictEqual(options.sortBy, SortBy.InstallCount);255});256257test('Test non empty query without sort doesnt use sortBy', async () => {258const target = <SinonStub>instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage());259await testableView.show('some extension');260assert.ok(target.calledOnce);261const options: IQueryOptions = target.args[0][0];262assert.strictEqual(options.sortBy, undefined);263});264265test('Test query with sort uses sortBy', async () => {266const target = <SinonStub>instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage());267await testableView.show('some extension @sort:rating');268assert.ok(target.calledOnce);269const options: IQueryOptions = target.args[0][0];270assert.strictEqual(options.sortBy, SortBy.WeightedRating);271});272273test('Test default view actions required sorting', async () => {274queryPage = aPage([aGalleryExtension(localEnabledLanguage.manifest.name, { ...localEnabledLanguage.manifest, version: '1.0.1', identifier: localDisabledLanguage.identifier })]);275276const workbenchService = instantiationService.get(IExtensionsWorkbenchService);277const extension = (await workbenchService.queryLocal()).find(ex => ex.identifier.id === localEnabledLanguage.identifier.id);278279await new Promise<void>(c => {280const disposable = workbenchService.onChange(() => {281if (extension?.outdated) {282disposable.dispose();283c();284}285});286instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None);287});288289const result = await testableView.show('@installed');290assert.strictEqual(result.length, 5, 'Unexpected number of results for @installed query');291const actual = [result.get(0).name, result.get(1).name, result.get(2).name, result.get(3).name, result.get(4).name];292const expected = [localEnabledLanguage.manifest.name, localEnabledTheme.manifest.name, localRandom.manifest.name, localDisabledTheme.manifest.name, localDisabledLanguage.manifest.name];293for (let i = 0; i < result.length; i++) {294assert.strictEqual(actual[i], expected[i], 'Unexpected extension for @installed query with outadted extension.');295}296});297298test('Test installed query results', async () => {299await testableView.show('@installed').then(result => {300assert.strictEqual(result.length, 5, 'Unexpected number of results for @installed query');301const actual = [result.get(0).name, result.get(1).name, result.get(2).name, result.get(3).name, result.get(4).name].sort();302const expected = [localDisabledTheme.manifest.name, localEnabledTheme.manifest.name, localRandom.manifest.name, localDisabledLanguage.manifest.name, localEnabledLanguage.manifest.name];303for (let i = 0; i < result.length; i++) {304assert.strictEqual(actual[i], expected[i], 'Unexpected extension for @installed query.');305}306});307308await testableView.show('@installed first').then(result => {309assert.strictEqual(result.length, 2, 'Unexpected number of results for @installed query');310assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');311assert.strictEqual(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');312});313314await testableView.show('@disabled').then(result => {315assert.strictEqual(result.length, 2, 'Unexpected number of results for @disabled query');316assert.strictEqual(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @disabled query.');317assert.strictEqual(result.get(1).name, localDisabledLanguage.manifest.name, 'Unexpected extension for @disabled query.');318});319320await testableView.show('@enabled').then(result => {321assert.strictEqual(result.length, 3, 'Unexpected number of results for @enabled query');322assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @enabled query.');323assert.strictEqual(result.get(1).name, localRandom.manifest.name, 'Unexpected extension for @enabled query.');324assert.strictEqual(result.get(2).name, localEnabledLanguage.manifest.name, 'Unexpected extension for @enabled query.');325});326327await testableView.show('@builtin category:themes').then(result => {328assert.strictEqual(result.length, 1, 'Unexpected number of results for @builtin category:themes query');329assert.strictEqual(result.get(0).name, builtInTheme.manifest.name, 'Unexpected extension for @builtin:themes query.');330});331332await testableView.show('@builtin category:"programming languages"').then(result => {333assert.strictEqual(result.length, 1, 'Unexpected number of results for @builtin:basics query');334assert.strictEqual(result.get(0).name, builtInBasic.manifest.name, 'Unexpected extension for @builtin:basics query.');335});336337await testableView.show('@builtin').then(result => {338assert.strictEqual(result.length, 2, 'Unexpected number of results for @builtin query');339assert.strictEqual(result.get(0).name, builtInBasic.manifest.name, 'Unexpected extension for @builtin query.');340assert.strictEqual(result.get(1).name, builtInTheme.manifest.name, 'Unexpected extension for @builtin query.');341});342343await testableView.show('@builtin my-theme').then(result => {344assert.strictEqual(result.length, 1, 'Unexpected number of results for @builtin query');345assert.strictEqual(result.get(0).name, builtInTheme.manifest.name, 'Unexpected extension for @builtin query.');346});347});348349test('Test installed query with category', async () => {350await testableView.show('@installed category:themes').then(result => {351assert.strictEqual(result.length, 2, 'Unexpected number of results for @installed query with category');352assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');353assert.strictEqual(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');354});355356await testableView.show('@installed category:"themes"').then(result => {357assert.strictEqual(result.length, 2, 'Unexpected number of results for @installed query with quoted category');358assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');359assert.strictEqual(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');360});361362await testableView.show('@installed category:"programming languages"').then(result => {363assert.strictEqual(result.length, 2, 'Unexpected number of results for @installed query with quoted category including space');364assert.strictEqual(result.get(0).name, localEnabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category including space.');365assert.strictEqual(result.get(1).name, localDisabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category inlcuding space.');366});367368await testableView.show('@installed category:themes category:random').then(result => {369assert.strictEqual(result.length, 3, 'Unexpected number of results for @installed query with multiple category');370assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');371assert.strictEqual(result.get(1).name, localRandom.manifest.name, 'Unexpected extension for @installed query with multiple category.');372assert.strictEqual(result.get(2).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');373});374375await testableView.show('@enabled category:themes').then(result => {376assert.strictEqual(result.length, 1, 'Unexpected number of results for @enabled query with category');377assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @enabled query with category.');378});379380await testableView.show('@enabled category:"themes"').then(result => {381assert.strictEqual(result.length, 1, 'Unexpected number of results for @enabled query with quoted category');382assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @enabled query with quoted category.');383});384385await testableView.show('@enabled category:"programming languages"').then(result => {386assert.strictEqual(result.length, 1, 'Unexpected number of results for @enabled query with quoted category inlcuding space');387assert.strictEqual(result.get(0).name, localEnabledLanguage.manifest.name, 'Unexpected extension for @enabled query with quoted category including space.');388});389390await testableView.show('@disabled category:themes').then(result => {391assert.strictEqual(result.length, 1, 'Unexpected number of results for @disabled query with category');392assert.strictEqual(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @disabled query with category.');393});394395await testableView.show('@disabled category:"themes"').then(result => {396assert.strictEqual(result.length, 1, 'Unexpected number of results for @disabled query with quoted category');397assert.strictEqual(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @disabled query with quoted category.');398});399400await testableView.show('@disabled category:"programming languages"').then(result => {401assert.strictEqual(result.length, 1, 'Unexpected number of results for @disabled query with quoted category inlcuding space');402assert.strictEqual(result.get(0).name, localDisabledLanguage.manifest.name, 'Unexpected extension for @disabled query with quoted category including space.');403});404});405406test('Test local query with sorting order', async () => {407await testableView.show('@recentlyUpdated').then(result => {408assert.strictEqual(result.length, 1, 'Unexpected number of results for @recentlyUpdated');409assert.strictEqual(result.get(0).name, localDisabledLanguage.manifest.name, 'Unexpected default sort order of extensions for @recentlyUpdate query');410});411412await testableView.show('@installed @sort:updateDate').then(result => {413assert.strictEqual(result.length, 5, 'Unexpected number of results for @sort:updateDate. Expected all localy installed Extension which are not builtin');414const actual = [result.get(0).local?.installedTimestamp, result.get(1).local?.installedTimestamp, result.get(2).local?.installedTimestamp, result.get(3).local?.installedTimestamp, result.get(4).local?.installedTimestamp];415const expected = [localEnabledLanguage.installedTimestamp, localDisabledLanguage.installedTimestamp, localRandom.installedTimestamp, localDisabledTheme.installedTimestamp, localEnabledTheme.installedTimestamp];416for (let i = 0; i < result.length; i++) {417assert.strictEqual(actual[i], expected[i], 'Unexpected extension sorting for @sort:updateDate query.');418}419});420});421422test('Test @recommended:workspace query', () => {423const workspaceRecommendedExtensions = [424workspaceRecommendationA,425workspaceRecommendationB,426configBasedRecommendationA,427];428429return testableView.show('@recommended:workspace').then(result => {430assert.strictEqual(result.length, workspaceRecommendedExtensions.length);431for (let i = 0; i < workspaceRecommendedExtensions.length; i++) {432assert.strictEqual(result.get(i).identifier.id, workspaceRecommendedExtensions[i].identifier.id);433}434});435});436437test('Test @recommended query', async () => {438const allRecommendedExtensions = [439fileBasedRecommendationA,440fileBasedRecommendationB,441configBasedRecommendationB,442otherRecommendationA443];444445const result = await testableView.show('@recommended');446assert.strictEqual(result.length, allRecommendedExtensions.length);447for (let i = 0; i < allRecommendedExtensions.length; i++) {448assert.strictEqual(result.get(i).identifier.id, allRecommendedExtensions[i].identifier.id);449}450});451452453test('Test @recommended:all query', async () => {454const allRecommendedExtensions = [455workspaceRecommendationA,456workspaceRecommendationB,457configBasedRecommendationA,458fileBasedRecommendationA,459fileBasedRecommendationB,460configBasedRecommendationB,461otherRecommendationA,462];463464const result = await testableView.show('@recommended:all');465assert.strictEqual(result.length, allRecommendedExtensions.length);466for (let i = 0; i < allRecommendedExtensions.length; i++) {467assert.strictEqual(result.get(i).identifier.id, allRecommendedExtensions[i].identifier.id);468}469});470471test('Test search', async () => {472const results = [473fileBasedRecommendationA,474workspaceRecommendationA,475otherRecommendationA,476workspaceRecommendationB477];478queryPage = aPage(results);479const result = await testableView.show('search-me');480assert.strictEqual(result.length, results.length);481for (let i = 0; i < results.length; i++) {482assert.strictEqual(result.get(i).identifier.id, results[i].identifier.id);483}484});485486test('Test preferred search experiment', async () => {487queryPage = aPage([488fileBasedRecommendationA,489workspaceRecommendationA,490otherRecommendationA,491workspaceRecommendationB492], 5);493const notInFirstPage = aGalleryExtension('not-in-first-page');494galleryExtensions.push(notInFirstPage);495const expected = [496workspaceRecommendationA,497notInFirstPage,498workspaceRecommendationB,499fileBasedRecommendationA,500otherRecommendationA,501];502503instantiationService.stubPromise(IWorkbenchExtensionManagementService, 'getExtensionsControlManifest', {504malicious: [], deprecated: {},505search: [{506query: 'search-me',507preferredResults: [508workspaceRecommendationA.identifier.id,509notInFirstPage.identifier.id,510workspaceRecommendationB.identifier.id511]512}]513});514515const testObject = disposableStore.add(instantiationService.createInstance(ExtensionsListView, {}, { id: '', title: '' }));516const result = await testObject.show('search-me');517assert.strictEqual(result.length, expected.length);518for (let i = 0; i < expected.length; i++) {519assert.strictEqual(result.get(i).identifier.id, expected[i].identifier.id);520}521});522523test('Skip preferred search experiment when user defines sort order', async () => {524const realResults = [525fileBasedRecommendationA,526workspaceRecommendationA,527otherRecommendationA,528workspaceRecommendationB529];530queryPage = aPage(realResults);531532const result = await testableView.show('search-me @sort:installs');533assert.strictEqual(result.length, realResults.length);534for (let i = 0; i < realResults.length; i++) {535assert.strictEqual(result.get(i).identifier.id, realResults[i].identifier.id);536}537});538539function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension {540manifest = { name, publisher: 'pub', version: '1.0.0', ...manifest };541properties = {542type: ExtensionType.User,543location: URI.file(`pub.${name}`),544identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) },545metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' },546...properties,547isValid: properties.isValid ?? true,548};549properties.isBuiltin = properties.type === ExtensionType.System;550return <ILocalExtension>Object.create({ manifest, ...properties });551}552553function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: any = {}): IGalleryExtension {554const targetPlatform = getTargetPlatform(platform, arch);555const galleryExtension = <IGalleryExtension>Object.create({ name, publisher: 'pub', version: '1.0.0', allTargetPlatforms: [targetPlatform], properties: {}, assets: {}, ...properties });556galleryExtension.properties = { ...galleryExtension.properties, dependencies: [], targetPlatform, ...galleryExtensionProperties };557galleryExtension.assets = { ...galleryExtension.assets, ...assets };558galleryExtension.identifier = { id: getGalleryExtensionId(galleryExtension.publisher, galleryExtension.name), uuid: generateUuid() };559return <IGalleryExtension>galleryExtension;560}561562function aPage<T>(objects: IGalleryExtension[] = [], total?: number): IPager<IGalleryExtension> {563return { firstPage: objects, total: total ?? objects.length, pageSize: objects.length, getPage: () => null! };564}565566});567568569