Path: blob/main/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts
5272 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 { IMeteredConnectionService } from '../../../../../platform/meteredConnection/common/meteredConnection.js';52import { IFileService } from '../../../../../platform/files/common/files.js';53import { FileService } from '../../../../../platform/files/common/fileService.js';54import { IUserDataProfileService } from '../../../../services/userDataProfile/common/userDataProfile.js';55import { UserDataProfileService } from '../../../../services/userDataProfile/common/userDataProfileService.js';56import { toUserDataProfile } from '../../../../../platform/userDataProfile/common/userDataProfile.js';5758suite('ExtensionsViews Tests', () => {5960const disposableStore = ensureNoDisposablesAreLeakedInTestSuite();6162let instantiationService: TestInstantiationService;63let testableView: ExtensionsListView;6465const localEnabledTheme = aLocalExtension('first-enabled-extension', { categories: ['Themes', 'random'] }, { installedTimestamp: 123456 });66const localEnabledLanguage = aLocalExtension('second-enabled-extension', { categories: ['Programming languages'], version: '1.0.0' }, { installedTimestamp: Date.now(), updated: false });67const localDisabledTheme = aLocalExtension('first-disabled-extension', { categories: ['themes'] }, { installedTimestamp: 234567 });68const localDisabledLanguage = aLocalExtension('second-disabled-extension', { categories: ['programming languages'] }, { installedTimestamp: Date.now() - 50000, updated: true });69const localRandom = aLocalExtension('random-enabled-extension', { categories: ['random'] }, { installedTimestamp: 345678 });70const builtInTheme = aLocalExtension('my-theme', { categories: ['Themes'], contributes: { themes: ['my-theme'] } }, { type: ExtensionType.System, installedTimestamp: 222 });71const builtInBasic = aLocalExtension('my-lang', { categories: ['Programming Languages'], contributes: { grammars: [{ language: 'my-language' }] } }, { type: ExtensionType.System, installedTimestamp: 666666 });7273let queryPage = aPage([]);74const galleryExtensions: IGalleryExtension[] = [];7576const workspaceRecommendationA = aGalleryExtension('workspace-recommendation-A');77const workspaceRecommendationB = aGalleryExtension('workspace-recommendation-B');78const configBasedRecommendationA = aGalleryExtension('configbased-recommendation-A');79const configBasedRecommendationB = aGalleryExtension('configbased-recommendation-B');80const fileBasedRecommendationA = aGalleryExtension('filebased-recommendation-A');81const fileBasedRecommendationB = aGalleryExtension('filebased-recommendation-B');82const otherRecommendationA = aGalleryExtension('other-recommendation-A');8384setup(async () => {85instantiationService = disposableStore.add(new TestInstantiationService());86instantiationService.stub(ITelemetryService, NullTelemetryService);87instantiationService.stub(ILogService, NullLogService);88instantiationService.stub(IFileService, disposableStore.add(new FileService(new NullLogService())));89instantiationService.stub(IProductService, {});9091instantiationService.stub(IWorkspaceContextService, new TestContextService());92instantiationService.stub(IConfigurationService, new TestConfigurationService());9394instantiationService.stub(IExtensionGalleryService, ExtensionGalleryService);95instantiationService.stub(ISharedProcessService, TestSharedProcessService);9697instantiationService.stub(IWorkbenchExtensionManagementService, {98onInstallExtension: Event.None,99onDidInstallExtensions: Event.None,100onUninstallExtension: Event.None,101onDidUninstallExtension: Event.None,102onDidUpdateExtensionMetadata: Event.None,103onDidChangeProfile: Event.None,104onProfileAwareDidInstallExtensions: Event.None,105async getInstalled() { return []; },106async getInstalledWorkspaceExtensions() { return []; },107async canInstall() { return true; },108async getExtensionsControlManifest() { return { malicious: [], deprecated: {}, search: [], publisherMapping: {} }; },109async getTargetPlatform() { return getTargetPlatform(platform, arch); },110async updateMetadata(local) { return local; }111});112instantiationService.stub(IRemoteAgentService, RemoteAgentService);113instantiationService.stub(IContextKeyService, new MockContextKeyService());114instantiationService.stub(IMenuService, new TestMenuService());115116const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService, label: 'local', id: 'vscode-local' };117instantiationService.stub(IExtensionManagementServerService, {118get localExtensionManagementServer(): IExtensionManagementServer {119return localExtensionManagementServer;120},121getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null {122if (extension.location.scheme === Schemas.file) {123return localExtensionManagementServer;124}125throw new Error(`Invalid Extension ${extension.location}`);126}127});128129instantiationService.stub(IWorkbenchExtensionEnablementService, disposableStore.add(new TestExtensionEnablementService(instantiationService)));130instantiationService.stub(IUserDataProfileService, disposableStore.add(new UserDataProfileService(toUserDataProfile('test', 'test', URI.file('foo'), URI.file('cache')))));131132const reasons: { [key: string]: any } = {};133reasons[workspaceRecommendationA.identifier.id] = { reasonId: ExtensionRecommendationReason.Workspace };134reasons[workspaceRecommendationB.identifier.id] = { reasonId: ExtensionRecommendationReason.Workspace };135reasons[fileBasedRecommendationA.identifier.id] = { reasonId: ExtensionRecommendationReason.File };136reasons[fileBasedRecommendationB.identifier.id] = { reasonId: ExtensionRecommendationReason.File };137reasons[otherRecommendationA.identifier.id] = { reasonId: ExtensionRecommendationReason.Executable };138reasons[configBasedRecommendationA.identifier.id] = { reasonId: ExtensionRecommendationReason.WorkspaceConfig };139instantiationService.stub(IExtensionRecommendationsService, {140getWorkspaceRecommendations() {141return Promise.resolve([142workspaceRecommendationA.identifier.id,143workspaceRecommendationB.identifier.id]);144},145getConfigBasedRecommendations() {146return Promise.resolve({147important: [configBasedRecommendationA.identifier.id],148others: [configBasedRecommendationB.identifier.id],149});150},151getImportantRecommendations(): Promise<string[]> {152return Promise.resolve([]);153},154getFileBasedRecommendations() {155return [156fileBasedRecommendationA.identifier.id,157fileBasedRecommendationB.identifier.id158];159},160getOtherRecommendations() {161return Promise.resolve([162configBasedRecommendationB.identifier.id,163otherRecommendationA.identifier.id164]);165},166getAllRecommendationsWithReason() {167return reasons;168}169});170instantiationService.stub(IURLService, NativeURLService);171172instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [localEnabledTheme, localEnabledLanguage, localRandom, localDisabledTheme, localDisabledLanguage, builtInTheme, builtInBasic]);173instantiationService.stubPromise(IExtensionManagementService, 'getExtensgetExtensionsControlManifestionsReport', {});174175instantiationService.stub(IExtensionGalleryService, <Partial<IExtensionGalleryService>>{176query: async () => {177return queryPage;178},179getCompatibleExtension: async (gallery) => {180return gallery;181},182getExtensions: async (infos) => {183const result: IGalleryExtension[] = [];184for (const info of infos) {185const extension = galleryExtensions.find(e => e.identifier.id === info.id);186if (extension) {187result.push(extension);188}189}190return result;191},192isEnabled: () => true,193isExtensionCompatible: async () => true,194});195196instantiationService.stub(IViewDescriptorService, {197getViewLocationById(): ViewContainerLocation {198return ViewContainerLocation.Sidebar;199},200onDidChangeLocation: Event.None201});202203instantiationService.stub(IExtensionService, {204onDidChangeExtensions: Event.None,205extensions: [206toExtensionDescription(localEnabledTheme),207toExtensionDescription(localEnabledLanguage),208toExtensionDescription(localRandom),209toExtensionDescription(builtInTheme),210toExtensionDescription(builtInBasic)211],212canAddExtension: (extension) => true,213whenInstalledExtensionsRegistered: () => Promise.resolve(true)214});215await (<TestExtensionEnablementService>instantiationService.get(IWorkbenchExtensionEnablementService)).setEnablement([localDisabledTheme], EnablementState.DisabledGlobally);216await (<TestExtensionEnablementService>instantiationService.get(IWorkbenchExtensionEnablementService)).setEnablement([localDisabledLanguage], EnablementState.DisabledGlobally);217218instantiationService.stub(IUpdateService, { onStateChange: Event.None, state: State.Uninitialized });219instantiationService.stub(IMeteredConnectionService, { isConnectionMetered: false, onDidChangeIsConnectionMetered: Event.None });220instantiationService.set(IExtensionsWorkbenchService, disposableStore.add(instantiationService.createInstance(ExtensionsWorkbenchService)));221testableView = disposableStore.add(instantiationService.createInstance(ExtensionsListView, {}, { id: '', title: '' }));222queryPage = aPage([]);223224galleryExtensions.splice(0, galleryExtensions.length, ...[225workspaceRecommendationA,226workspaceRecommendationB,227configBasedRecommendationA,228configBasedRecommendationB,229fileBasedRecommendationA,230fileBasedRecommendationB,231otherRecommendationA232]);233});234235test('Test query types', () => {236assert.strictEqual(ExtensionsListView.isBuiltInExtensionsQuery('@builtin'), true);237assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@installed'), true);238assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@enabled'), true);239assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@disabled'), true);240assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@outdated'), true);241assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@updates'), true);242assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@sort:name'), true);243assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@sort:updateDate'), true);244assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@installed searchText'), true);245assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@enabled searchText'), true);246assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@disabled searchText'), true);247assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@outdated searchText'), true);248assert.strictEqual(ExtensionsListView.isLocalExtensionsQuery('@updates searchText'), true);249});250251test('Test empty query equates to sort by install count', async () => {252const target = <SinonStub>instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage());253await testableView.show('');254assert.ok(target.calledOnce);255const options: IQueryOptions = target.args[0][0];256assert.strictEqual(options.sortBy, SortBy.InstallCount);257});258259test('Test non empty query without sort doesnt use sortBy', async () => {260const target = <SinonStub>instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage());261await testableView.show('some extension');262assert.ok(target.calledOnce);263const options: IQueryOptions = target.args[0][0];264assert.strictEqual(options.sortBy, undefined);265});266267test('Test query with sort uses sortBy', async () => {268const target = <SinonStub>instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage());269await testableView.show('some extension @sort:rating');270assert.ok(target.calledOnce);271const options: IQueryOptions = target.args[0][0];272assert.strictEqual(options.sortBy, SortBy.WeightedRating);273});274275test('Test default view actions required sorting', async () => {276queryPage = aPage([aGalleryExtension(localEnabledLanguage.manifest.name, { ...localEnabledLanguage.manifest, version: '1.0.1', identifier: localDisabledLanguage.identifier })]);277278const workbenchService = instantiationService.get(IExtensionsWorkbenchService);279const extension = (await workbenchService.queryLocal()).find(ex => ex.identifier.id === localEnabledLanguage.identifier.id);280281await new Promise<void>(c => {282const disposable = workbenchService.onChange(() => {283if (extension?.outdated) {284disposable.dispose();285c();286}287});288instantiationService.get(IExtensionsWorkbenchService).queryGallery(CancellationToken.None);289});290291const result = await testableView.show('@installed');292assert.strictEqual(result.length, 5, 'Unexpected number of results for @installed query');293const actual = [result.get(0).name, result.get(1).name, result.get(2).name, result.get(3).name, result.get(4).name];294const expected = [localEnabledLanguage.manifest.name, localEnabledTheme.manifest.name, localRandom.manifest.name, localDisabledTheme.manifest.name, localDisabledLanguage.manifest.name];295for (let i = 0; i < result.length; i++) {296assert.strictEqual(actual[i], expected[i], 'Unexpected extension for @installed query with outadted extension.');297}298});299300test('Test installed query results', async () => {301await testableView.show('@installed').then(result => {302assert.strictEqual(result.length, 5, 'Unexpected number of results for @installed query');303const actual = [result.get(0).name, result.get(1).name, result.get(2).name, result.get(3).name, result.get(4).name].sort();304const expected = [localDisabledTheme.manifest.name, localEnabledTheme.manifest.name, localRandom.manifest.name, localDisabledLanguage.manifest.name, localEnabledLanguage.manifest.name];305for (let i = 0; i < result.length; i++) {306assert.strictEqual(actual[i], expected[i], 'Unexpected extension for @installed query.');307}308});309310await testableView.show('@installed first').then(result => {311assert.strictEqual(result.length, 2, 'Unexpected number of results for @installed query');312assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');313assert.strictEqual(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with search text.');314});315316await testableView.show('@disabled').then(result => {317assert.strictEqual(result.length, 2, 'Unexpected number of results for @disabled query');318assert.strictEqual(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @disabled query.');319assert.strictEqual(result.get(1).name, localDisabledLanguage.manifest.name, 'Unexpected extension for @disabled query.');320});321322await testableView.show('@enabled').then(result => {323assert.strictEqual(result.length, 3, 'Unexpected number of results for @enabled query');324assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @enabled query.');325assert.strictEqual(result.get(1).name, localRandom.manifest.name, 'Unexpected extension for @enabled query.');326assert.strictEqual(result.get(2).name, localEnabledLanguage.manifest.name, 'Unexpected extension for @enabled query.');327});328329await testableView.show('@builtin category:themes').then(result => {330assert.strictEqual(result.length, 1, 'Unexpected number of results for @builtin category:themes query');331assert.strictEqual(result.get(0).name, builtInTheme.manifest.name, 'Unexpected extension for @builtin:themes query.');332});333334await testableView.show('@builtin category:"programming languages"').then(result => {335assert.strictEqual(result.length, 1, 'Unexpected number of results for @builtin:basics query');336assert.strictEqual(result.get(0).name, builtInBasic.manifest.name, 'Unexpected extension for @builtin:basics query.');337});338339await testableView.show('@builtin').then(result => {340assert.strictEqual(result.length, 2, 'Unexpected number of results for @builtin query');341assert.strictEqual(result.get(0).name, builtInBasic.manifest.name, 'Unexpected extension for @builtin query.');342assert.strictEqual(result.get(1).name, builtInTheme.manifest.name, 'Unexpected extension for @builtin query.');343});344345await testableView.show('@builtin my-theme').then(result => {346assert.strictEqual(result.length, 1, 'Unexpected number of results for @builtin query');347assert.strictEqual(result.get(0).name, builtInTheme.manifest.name, 'Unexpected extension for @builtin query.');348});349});350351test('Test installed query with category', async () => {352await testableView.show('@installed category:themes').then(result => {353assert.strictEqual(result.length, 2, 'Unexpected number of results for @installed query with category');354assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');355assert.strictEqual(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with category.');356});357358await testableView.show('@installed category:"themes"').then(result => {359assert.strictEqual(result.length, 2, 'Unexpected number of results for @installed query with quoted category');360assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');361assert.strictEqual(result.get(1).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with quoted category.');362});363364await testableView.show('@installed category:"programming languages"').then(result => {365assert.strictEqual(result.length, 2, 'Unexpected number of results for @installed query with quoted category including space');366assert.strictEqual(result.get(0).name, localEnabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category including space.');367assert.strictEqual(result.get(1).name, localDisabledLanguage.manifest.name, 'Unexpected extension for @installed query with quoted category inlcuding space.');368});369370await testableView.show('@installed category:themes category:random').then(result => {371assert.strictEqual(result.length, 3, 'Unexpected number of results for @installed query with multiple category');372assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');373assert.strictEqual(result.get(1).name, localRandom.manifest.name, 'Unexpected extension for @installed query with multiple category.');374assert.strictEqual(result.get(2).name, localDisabledTheme.manifest.name, 'Unexpected extension for @installed query with multiple category.');375});376377await testableView.show('@enabled category:themes').then(result => {378assert.strictEqual(result.length, 1, 'Unexpected number of results for @enabled query with category');379assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @enabled query with category.');380});381382await testableView.show('@enabled category:"themes"').then(result => {383assert.strictEqual(result.length, 1, 'Unexpected number of results for @enabled query with quoted category');384assert.strictEqual(result.get(0).name, localEnabledTheme.manifest.name, 'Unexpected extension for @enabled query with quoted category.');385});386387await testableView.show('@enabled category:"programming languages"').then(result => {388assert.strictEqual(result.length, 1, 'Unexpected number of results for @enabled query with quoted category inlcuding space');389assert.strictEqual(result.get(0).name, localEnabledLanguage.manifest.name, 'Unexpected extension for @enabled query with quoted category including space.');390});391392await testableView.show('@disabled category:themes').then(result => {393assert.strictEqual(result.length, 1, 'Unexpected number of results for @disabled query with category');394assert.strictEqual(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @disabled query with category.');395});396397await testableView.show('@disabled category:"themes"').then(result => {398assert.strictEqual(result.length, 1, 'Unexpected number of results for @disabled query with quoted category');399assert.strictEqual(result.get(0).name, localDisabledTheme.manifest.name, 'Unexpected extension for @disabled query with quoted category.');400});401402await testableView.show('@disabled category:"programming languages"').then(result => {403assert.strictEqual(result.length, 1, 'Unexpected number of results for @disabled query with quoted category inlcuding space');404assert.strictEqual(result.get(0).name, localDisabledLanguage.manifest.name, 'Unexpected extension for @disabled query with quoted category including space.');405});406});407408test('Test local query with sorting order', async () => {409await testableView.show('@recentlyUpdated').then(result => {410assert.strictEqual(result.length, 1, 'Unexpected number of results for @recentlyUpdated');411assert.strictEqual(result.get(0).name, localDisabledLanguage.manifest.name, 'Unexpected default sort order of extensions for @recentlyUpdate query');412});413414await testableView.show('@installed @sort:updateDate').then(result => {415assert.strictEqual(result.length, 5, 'Unexpected number of results for @sort:updateDate. Expected all localy installed Extension which are not builtin');416const 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];417const expected = [localEnabledLanguage.installedTimestamp, localDisabledLanguage.installedTimestamp, localRandom.installedTimestamp, localDisabledTheme.installedTimestamp, localEnabledTheme.installedTimestamp];418for (let i = 0; i < result.length; i++) {419assert.strictEqual(actual[i], expected[i], 'Unexpected extension sorting for @sort:updateDate query.');420}421});422});423424test('Test @recommended:workspace query', () => {425const workspaceRecommendedExtensions = [426workspaceRecommendationA,427workspaceRecommendationB,428configBasedRecommendationA,429];430431return testableView.show('@recommended:workspace').then(result => {432assert.strictEqual(result.length, workspaceRecommendedExtensions.length);433for (let i = 0; i < workspaceRecommendedExtensions.length; i++) {434assert.strictEqual(result.get(i).identifier.id, workspaceRecommendedExtensions[i].identifier.id);435}436});437});438439test('Test @recommended query', async () => {440const allRecommendedExtensions = [441fileBasedRecommendationA,442fileBasedRecommendationB,443configBasedRecommendationB,444otherRecommendationA445];446447const result = await testableView.show('@recommended');448assert.strictEqual(result.length, allRecommendedExtensions.length);449for (let i = 0; i < allRecommendedExtensions.length; i++) {450assert.strictEqual(result.get(i).identifier.id, allRecommendedExtensions[i].identifier.id);451}452});453454455test('Test @recommended:all query', async () => {456const allRecommendedExtensions = [457workspaceRecommendationA,458workspaceRecommendationB,459configBasedRecommendationA,460fileBasedRecommendationA,461fileBasedRecommendationB,462configBasedRecommendationB,463otherRecommendationA,464];465466const result = await testableView.show('@recommended:all');467assert.strictEqual(result.length, allRecommendedExtensions.length);468for (let i = 0; i < allRecommendedExtensions.length; i++) {469assert.strictEqual(result.get(i).identifier.id, allRecommendedExtensions[i].identifier.id);470}471});472473test('Test search', async () => {474const results = [475fileBasedRecommendationA,476workspaceRecommendationA,477otherRecommendationA,478workspaceRecommendationB479];480queryPage = aPage(results);481const result = await testableView.show('search-me');482assert.strictEqual(result.length, results.length);483for (let i = 0; i < results.length; i++) {484assert.strictEqual(result.get(i).identifier.id, results[i].identifier.id);485}486});487488test('Test preferred search experiment', async () => {489queryPage = aPage([490fileBasedRecommendationA,491workspaceRecommendationA,492otherRecommendationA,493workspaceRecommendationB494], 5);495const notInFirstPage = aGalleryExtension('not-in-first-page');496galleryExtensions.push(notInFirstPage);497const expected = [498workspaceRecommendationA,499notInFirstPage,500workspaceRecommendationB,501fileBasedRecommendationA,502otherRecommendationA,503];504505instantiationService.stubPromise(IWorkbenchExtensionManagementService, 'getExtensionsControlManifest', {506malicious: [], deprecated: {},507search: [{508query: 'search-me',509preferredResults: [510workspaceRecommendationA.identifier.id,511notInFirstPage.identifier.id,512workspaceRecommendationB.identifier.id513]514}]515});516517const testObject = disposableStore.add(instantiationService.createInstance(ExtensionsListView, {}, { id: '', title: '' }));518const result = await testObject.show('search-me');519assert.strictEqual(result.length, expected.length);520for (let i = 0; i < expected.length; i++) {521assert.strictEqual(result.get(i).identifier.id, expected[i].identifier.id);522}523});524525test('Skip preferred search experiment when user defines sort order', async () => {526const realResults = [527fileBasedRecommendationA,528workspaceRecommendationA,529otherRecommendationA,530workspaceRecommendationB531];532queryPage = aPage(realResults);533534const result = await testableView.show('search-me @sort:installs');535assert.strictEqual(result.length, realResults.length);536for (let i = 0; i < realResults.length; i++) {537assert.strictEqual(result.get(i).identifier.id, realResults[i].identifier.id);538}539});540541function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension {542manifest = { name, publisher: 'pub', version: '1.0.0', ...manifest };543properties = {544type: ExtensionType.User,545location: URI.file(`pub.${name}`),546identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name) },547metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' },548...properties,549isValid: properties.isValid ?? true,550};551properties.isBuiltin = properties.type === ExtensionType.System;552return <ILocalExtension>Object.create({ manifest, ...properties });553}554555function aGalleryExtension(name: string, properties: any = {}, galleryExtensionProperties: any = {}, assets: any = {}): IGalleryExtension {556const targetPlatform = getTargetPlatform(platform, arch);557const galleryExtension = <IGalleryExtension>Object.create({ name, publisher: 'pub', version: '1.0.0', allTargetPlatforms: [targetPlatform], properties: {}, assets: {}, ...properties });558galleryExtension.properties = { ...galleryExtension.properties, dependencies: [], targetPlatform, ...galleryExtensionProperties };559galleryExtension.assets = { ...galleryExtension.assets, ...assets };560galleryExtension.identifier = { id: getGalleryExtensionId(galleryExtension.publisher, galleryExtension.name), uuid: generateUuid() };561return <IGalleryExtension>galleryExtension;562}563564function aPage<T>(objects: IGalleryExtension[] = [], total?: number): IPager<IGalleryExtension> {565return { firstPage: objects, total: total ?? objects.length, pageSize: objects.length, getPage: () => null! };566}567568});569570571