Vscode merge (#4582)

* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd

* fix issues with merges

* bump node version in azpipe

* replace license headers

* remove duplicate launch task

* fix build errors

* fix build errors

* fix tslint issues

* working through package and linux build issues

* more work

* wip

* fix packaged builds

* working through linux build errors

* wip

* wip

* wip

* fix mac and linux file limits

* iterate linux pipeline

* disable editor typing

* revert series to parallel

* remove optimize vscode from linux

* fix linting issues

* revert testing change

* add work round for new node

* readd packaging for extensions

* fix issue with angular not resolving decorator dependencies
This commit is contained in:
Anthony Dresser
2019-03-19 17:44:35 -07:00
committed by GitHub
parent 833d197412
commit 87765e8673
1879 changed files with 54505 additions and 38058 deletions

View File

@@ -0,0 +1,109 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { INotificationService, Severity, IPromptChoice } from 'vs/platform/notification/common/notification';
import { IExperimentService, IExperiment, ExperimentActionType, IExperimentActionPromptProperties, IExperimentActionPromptCommand, ExperimentState } from 'vs/workbench/contrib/experiments/node/experimentService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { language } from 'vs/base/common/platform';
export class ExperimentalPrompts extends Disposable implements IWorkbenchContribution {
private _disposables: IDisposable[] = [];
constructor(
@IExperimentService private readonly experimentService: IExperimentService,
@IViewletService private readonly viewletService: IViewletService,
@INotificationService private readonly notificationService: INotificationService,
@ITelemetryService private readonly telemetryService: ITelemetryService
) {
super();
this.experimentService.onExperimentEnabled(e => {
if (e.action && e.action.type === ExperimentActionType.Prompt && e.state === ExperimentState.Run) {
this.showExperimentalPrompts(e);
}
}, this, this._disposables);
}
private showExperimentalPrompts(experiment: IExperiment): void {
if (!experiment || !experiment.enabled || !experiment.action || experiment.state !== ExperimentState.Run) {
return;
}
const logTelemetry = (commandText?: string) => {
/* __GDPR__
"experimentalPrompts" : {
"experimentId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"commandText": { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
"cancelled": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
}
*/
this.telemetryService.publicLog('experimentalPrompts', {
experimentId: experiment.id,
commandText,
cancelled: !commandText
});
};
const actionProperties = (<IExperimentActionPromptProperties>experiment.action.properties);
const promptText = ExperimentalPrompts.getLocalizedText(actionProperties.promptText, language || '');
if (!actionProperties || !promptText) {
return;
}
if (!actionProperties.commands) {
actionProperties.commands = [];
}
const choices: IPromptChoice[] = actionProperties.commands.map((command: IExperimentActionPromptCommand) => {
const commandText = ExperimentalPrompts.getLocalizedText(command.text, language || '');
return {
label: commandText,
run: () => {
logTelemetry(commandText);
if (command.externalLink) {
window.open(command.externalLink);
} else if (command.curatedExtensionsKey && Array.isArray(command.curatedExtensionsList)) {
this.viewletService.openViewlet('workbench.view.extensions', true)
.then(viewlet => viewlet as IExtensionsViewlet)
.then(viewlet => {
if (viewlet) {
viewlet.search('curated:' + command.curatedExtensionsKey);
}
});
}
this.experimentService.markAsCompleted(experiment.id);
}
};
});
this.notificationService.prompt(Severity.Info, promptText, choices, {
onCancel: () => {
logTelemetry();
this.experimentService.markAsCompleted(experiment.id);
}
});
}
dispose() {
this._disposables = dispose(this._disposables);
}
static getLocalizedText(text: string | { [key: string]: string }, displayLanguage: string): string {
if (typeof text === 'string') {
return text;
}
const msgInEnglish = text['en'] || text['en-us'];
displayLanguage = displayLanguage.toLowerCase();
if (!text[displayLanguage] && displayLanguage.indexOf('-') === 2) {
displayLanguage = displayLanguage.substr(0, 2);
}
return text[displayLanguage] || msgInEnglish;
}
}

View File

@@ -0,0 +1,15 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExperimentService, ExperimentService } from 'vs/workbench/contrib/experiments/node/experimentService';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/electron-browser/experimentalPrompt';
registerSingleton(IExperimentService, ExperimentService, true);
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ExperimentalPrompts, LifecyclePhase.Eventually);

View File

@@ -0,0 +1,463 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import product from 'vs/platform/product/node/product';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IRequestService } from 'vs/platform/request/node/request';
import { language } from 'vs/base/common/platform';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { match } from 'vs/base/common/glob';
import { asJson } from 'vs/base/node/request';
import { Emitter, Event } from 'vs/base/common/event';
import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles';
import { WorkspaceStats } from 'vs/workbench/contrib/stats/node/workspaceStats';
import { CancellationToken } from 'vs/base/common/cancellation';
import { distinct } from 'vs/base/common/arrays';
import { lastSessionDateStorageKey } from 'vs/platform/telemetry/node/workbenchCommonProperties';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
interface IExperimentStorageState {
enabled: boolean;
state: ExperimentState;
editCount?: number;
lastEditedDate?: string;
}
export const enum ExperimentState {
Evaluating,
NoRun,
Run,
Complete
}
interface IRawExperiment {
id: string;
enabled?: boolean;
condition?: {
insidersOnly?: boolean;
newUser?: boolean;
displayLanguage?: string;
installedExtensions?: {
excludes?: string[];
includes?: string[];
},
fileEdits?: {
filePathPattern?: string;
workspaceIncludes?: string[];
workspaceExcludes?: string[];
minEditCount: number;
},
experimentsPreviouslyRun?: {
excludes?: string[];
includes?: string[];
}
userProbability?: number;
};
action?: IExperimentAction;
}
interface IExperimentAction {
type: ExperimentActionType;
properties: any;
}
export enum ExperimentActionType {
Custom = 'Custom',
Prompt = 'Prompt',
AddToRecommendations = 'AddToRecommendations',
ExtensionSearchResults = 'ExtensionSearchResults'
}
export interface IExperimentActionPromptProperties {
promptText: string | { [key: string]: string };
commands: IExperimentActionPromptCommand[];
}
export interface IExperimentActionPromptCommand {
text: string | { [key: string]: string };
externalLink?: string;
curatedExtensionsKey?: string;
curatedExtensionsList?: string[];
}
export interface IExperiment {
id: string;
enabled: boolean;
state: ExperimentState;
action?: IExperimentAction;
}
export interface IExperimentService {
_serviceBrand: any;
getExperimentById(id: string): Promise<IExperiment>;
getExperimentsByType(type: ExperimentActionType): Promise<IExperiment[]>;
getCuratedExtensionsList(curatedExtensionsKey: string): Promise<string[]>;
markAsCompleted(experimentId: string): void;
onExperimentEnabled: Event<IExperiment>;
}
export const IExperimentService = createDecorator<IExperimentService>('experimentService');
export class ExperimentService extends Disposable implements IExperimentService {
_serviceBrand: any;
private _experiments: IExperiment[] = [];
private _loadExperimentsPromise: Promise<void>;
private _curatedMapping = Object.create(null);
private _disposables: IDisposable[] = [];
private readonly _onExperimentEnabled = new Emitter<IExperiment>();
onExperimentEnabled: Event<IExperiment> = this._onExperimentEnabled.event;
constructor(
@IStorageService private readonly storageService: IStorageService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@ITextFileService private readonly textFileService: ITextFileService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IRequestService private readonly requestService: IRequestService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super();
this._loadExperimentsPromise = Promise.resolve(this.lifecycleService.when(LifecyclePhase.Eventually)).then(() => this.loadExperiments());
}
public getExperimentById(id: string): Promise<IExperiment> {
return this._loadExperimentsPromise.then(() => {
return this._experiments.filter(x => x.id === id)[0];
});
}
public getExperimentsByType(type: ExperimentActionType): Promise<IExperiment[]> {
return this._loadExperimentsPromise.then(() => {
if (type === ExperimentActionType.Custom) {
return this._experiments.filter(x => x.enabled && (!x.action || x.action.type === type));
}
return this._experiments.filter(x => x.enabled && x.action && x.action.type === type);
});
}
public getCuratedExtensionsList(curatedExtensionsKey: string): Promise<string[]> {
return this._loadExperimentsPromise.then(() => {
for (const experiment of this._experiments) {
if (experiment.enabled
&& experiment.state === ExperimentState.Run
&& this._curatedMapping[experiment.id]
&& this._curatedMapping[experiment.id].curatedExtensionsKey === curatedExtensionsKey) {
return this._curatedMapping[experiment.id].curatedExtensionsList;
}
}
return [];
});
}
public markAsCompleted(experimentId: string): void {
const storageKey = 'experiments.' + experimentId;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
experimentState.state = ExperimentState.Complete;
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL);
}
protected getExperiments(): Promise<IRawExperiment[]> {
if (!product.experimentsUrl || this.configurationService.getValue('workbench.enableExperiments') === false) {
return Promise.resolve([]);
}
return this.requestService.request({ type: 'GET', url: product.experimentsUrl }, CancellationToken.None).then(context => {
if (context.res.statusCode !== 200) {
return Promise.resolve(null);
}
return asJson(context).then(result => {
return result && Array.isArray(result['experiments']) ? result['experiments'] : [];
});
}, () => Promise.resolve(null));
}
private loadExperiments(): Promise<any> {
return this.getExperiments().then(rawExperiments => {
// Offline mode
if (!rawExperiments) {
const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []);
if (Array.isArray(allExperimentIdsFromStorage)) {
allExperimentIdsFromStorage.forEach(experimentId => {
const storageKey = 'experiments.' + experimentId;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), null);
if (experimentState) {
this._experiments.push({
id: experimentId,
enabled: experimentState.enabled,
state: experimentState.state
});
}
});
}
return Promise.resolve(null);
}
// Clear disbaled/deleted experiments from storage
const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []);
const enabledExperiments = rawExperiments.filter(experiment => !!experiment.enabled).map(experiment => experiment.id.toLowerCase());
if (Array.isArray(allExperimentIdsFromStorage)) {
allExperimentIdsFromStorage.forEach(experiment => {
if (enabledExperiments.indexOf(experiment) === -1) {
this.storageService.remove(`experiments.${experiment}`, StorageScope.GLOBAL);
}
});
}
if (enabledExperiments.length) {
this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL);
} else {
this.storageService.remove('allExperiments', StorageScope.GLOBAL);
}
const promises = rawExperiments.map(experiment => {
const processedExperiment: IExperiment = {
id: experiment.id,
enabled: !!experiment.enabled,
state: !!experiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun
};
if (experiment.action) {
processedExperiment.action = {
type: ExperimentActionType[experiment.action.type] || ExperimentActionType.Custom,
properties: experiment.action.properties
};
if (processedExperiment.action.type === ExperimentActionType.Prompt) {
((<IExperimentActionPromptProperties>processedExperiment.action.properties).commands || []).forEach(x => {
if (x.curatedExtensionsKey && Array.isArray(x.curatedExtensionsList)) {
this._curatedMapping[experiment.id] = x;
}
});
}
if (!processedExperiment.action.properties) {
processedExperiment.action.properties = {};
}
}
this._experiments.push(processedExperiment);
if (!processedExperiment.enabled) {
return Promise.resolve(null);
}
const storageKey = 'experiments.' + experiment.id;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
if (!experimentState.hasOwnProperty('enabled')) {
experimentState.enabled = processedExperiment.enabled;
}
if (!experimentState.hasOwnProperty('state')) {
experimentState.state = processedExperiment.enabled ? ExperimentState.Evaluating : ExperimentState.NoRun;
} else {
processedExperiment.state = experimentState.state;
}
return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => {
experimentState.state = processedExperiment.state = state;
this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL);
if (state === ExperimentState.Run) {
this.fireRunExperiment(processedExperiment);
}
return Promise.resolve(null);
});
});
return Promise.all(promises).then(() => {
/* __GDPR__
"experiments" : {
"experiments" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
}
*/
this.telemetryService.publicLog('experiments', { experiments: this._experiments });
});
});
}
private fireRunExperiment(experiment: IExperiment) {
this._onExperimentEnabled.fire(experiment);
const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []);
if (runExperimentIdsFromStorage.indexOf(experiment.id) === -1) {
runExperimentIdsFromStorage.push(experiment.id);
}
// Ensure we dont store duplicates
const distinctExperiments = distinct(runExperimentIdsFromStorage);
if (runExperimentIdsFromStorage.length !== distinctExperiments.length) {
this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL);
}
}
private checkExperimentDependencies(experiment: IRawExperiment): boolean {
const experimentsPreviouslyRun = experiment.condition ? experiment.condition.experimentsPreviouslyRun : undefined;
if (experimentsPreviouslyRun) {
const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []);
let includeCheck = true;
let excludeCheck = true;
const includes = experimentsPreviouslyRun.includes;
if (Array.isArray(includes)) {
includeCheck = runExperimentIdsFromStorage.some(x => includes.indexOf(x) > -1);
}
const excludes = experimentsPreviouslyRun.excludes;
if (includeCheck && Array.isArray(excludes)) {
excludeCheck = !runExperimentIdsFromStorage.some(x => excludes.indexOf(x) > -1);
}
if (!includeCheck || !excludeCheck) {
return false;
}
}
return true;
}
private shouldRunExperiment(experiment: IRawExperiment, processedExperiment: IExperiment): Promise<ExperimentState> {
if (processedExperiment.state !== ExperimentState.Evaluating) {
return Promise.resolve(processedExperiment.state);
}
if (!experiment.enabled) {
return Promise.resolve(ExperimentState.NoRun);
}
const condition = experiment.condition;
if (!condition) {
return Promise.resolve(ExperimentState.Run);
}
if (!this.checkExperimentDependencies(experiment)) {
return Promise.resolve(ExperimentState.NoRun);
}
if (this.environmentService.appQuality === 'stable' && condition.insidersOnly === true) {
return Promise.resolve(ExperimentState.NoRun);
}
const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL);
if ((condition.newUser === true && !isNewUser)
|| (condition.newUser === false && isNewUser)) {
return Promise.resolve(ExperimentState.NoRun);
}
if (typeof condition.displayLanguage === 'string') {
let localeToCheck = condition.displayLanguage.toLowerCase();
let displayLanguage = language!.toLowerCase();
if (localeToCheck !== displayLanguage) {
const a = displayLanguage.indexOf('-');
const b = localeToCheck.indexOf('-');
if (a > -1) {
displayLanguage = displayLanguage.substr(0, a);
}
if (b > -1) {
localeToCheck = localeToCheck.substr(0, b);
}
if (displayLanguage !== localeToCheck) {
return Promise.resolve(ExperimentState.NoRun);
}
}
}
if (!condition.userProbability) {
condition.userProbability = 1;
}
let extensionsCheckPromise = Promise.resolve(true);
const installedExtensions = condition.installedExtensions;
if (installedExtensions) {
extensionsCheckPromise = this.extensionManagementService.getInstalled(ExtensionType.User).then(locals => {
let includesCheck = true;
let excludesCheck = true;
const localExtensions = locals.map(local => `${local.manifest.publisher.toLowerCase()}.${local.manifest.name.toLowerCase()}`);
if (Array.isArray(installedExtensions.includes) && installedExtensions.includes.length) {
const extensionIncludes = installedExtensions.includes.map(e => e.toLowerCase());
includesCheck = localExtensions.some(e => extensionIncludes.indexOf(e) > -1);
}
if (Array.isArray(installedExtensions.excludes) && installedExtensions.excludes.length) {
const extensionExcludes = installedExtensions.excludes.map(e => e.toLowerCase());
excludesCheck = !localExtensions.some(e => extensionExcludes.indexOf(e) > -1);
}
return includesCheck && excludesCheck;
});
}
const storageKey = 'experiments.' + experiment.id;
const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
return extensionsCheckPromise.then(success => {
const fileEdits = condition.fileEdits;
if (!success || !fileEdits || typeof fileEdits.minEditCount !== 'number') {
const runExperiment = success && typeof condition.userProbability === 'number' && Math.random() < condition.userProbability;
return runExperiment ? ExperimentState.Run : ExperimentState.NoRun;
}
experimentState.editCount = experimentState.editCount || 0;
if (experimentState.editCount >= fileEdits.minEditCount) {
return ExperimentState.Run;
}
const onSaveHandler = this.textFileService.models.onModelsSaved(e => {
const date = new Date().toDateString();
const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {});
if (latestExperimentState.state !== ExperimentState.Evaluating) {
onSaveHandler.dispose();
return;
}
e.forEach(event => {
if (event.kind !== StateChange.SAVED
|| latestExperimentState.state !== ExperimentState.Evaluating
|| date === latestExperimentState.lastEditedDate
|| (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount)
) {
return;
}
let filePathCheck = true;
let workspaceCheck = true;
if (typeof fileEdits.filePathPattern === 'string') {
filePathCheck = match(fileEdits.filePathPattern, event.resource.fsPath);
}
if (Array.isArray(fileEdits.workspaceIncludes) && fileEdits.workspaceIncludes.length) {
workspaceCheck = !!WorkspaceStats.TAGS && fileEdits.workspaceIncludes.some(x => !!WorkspaceStats.TAGS[x]);
}
if (workspaceCheck && Array.isArray(fileEdits.workspaceExcludes) && fileEdits.workspaceExcludes.length) {
workspaceCheck = !!WorkspaceStats.TAGS && !fileEdits.workspaceExcludes.some(x => !!WorkspaceStats.TAGS[x]);
}
if (filePathCheck && workspaceCheck) {
latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1;
latestExperimentState.lastEditedDate = date;
this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL);
}
});
if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) {
processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun;
this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL);
if (latestExperimentState.state === ExperimentState.Run && experiment.action && ExperimentActionType[experiment.action.type] === ExperimentActionType.Prompt) {
this.fireRunExperiment(processedExperiment);
}
}
});
this._disposables.push(onSaveHandler);
return ExperimentState.Evaluating;
});
}
dispose() {
this._disposables = dispose(this._disposables);
}
}
function safeParse(text: string | undefined, defaultObject: any) {
try {
return text ? JSON.parse(text) || defaultObject : defaultObject;
} catch (e) {
return defaultObject;
}
}

View File

@@ -0,0 +1,819 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { ExperimentService, ExperimentActionType, ExperimentState, IExperiment } from 'vs/workbench/contrib/experiments/node/experimentService';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices';
import {
IExtensionManagementService, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier,
IExtensionEnablementService, ILocalExtension
} from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService';
import { Emitter } from 'vs/base/common/event';
import { TestExtensionEnablementService } from 'vs/platform/extensionManagement/test/electron-browser/extensionEnablementService.test';
import { URLService } from 'vs/platform/url/common/urlService';
import { IURLService } from 'vs/platform/url/common/url';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { assign } from 'vs/base/common/objects';
import { URI } from 'vs/base/common/uri';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { lastSessionDateStorageKey } from 'vs/platform/telemetry/node/workbenchCommonProperties';
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
interface ExperimentSettings {
enabled?: boolean;
id?: string;
state?: ExperimentState;
}
let experimentData: { [i: string]: any } = {
experiments: []
};
const local = aLocalExtension('installedExtension1', { version: '1.0.0' });
function aLocalExtension(name: string = 'someext', manifest: any = {}, properties: any = {}): ILocalExtension {
manifest = assign({ name, publisher: 'pub', version: '1.0.0' }, manifest);
properties = assign({
type: ExtensionType.User,
location: URI.file(`pub.${name}`),
identifier: { id: getGalleryExtensionId(manifest.publisher, manifest.name), uuid: undefined },
metadata: { id: getGalleryExtensionId(manifest.publisher, manifest.name), publisherId: manifest.publisher, publisherDisplayName: 'somename' }
}, properties);
return <ILocalExtension>Object.create({ manifest, ...properties });
}
export class TestExperimentService extends ExperimentService {
public getExperiments(): Promise<any[]> {
return Promise.resolve(experimentData.experiments);
}
}
suite('Experiment Service', () => {
let instantiationService: TestInstantiationService;
let testConfigurationService: TestConfigurationService;
let testObject: ExperimentService;
let installEvent: Emitter<InstallExtensionEvent>,
didInstallEvent: Emitter<DidInstallExtensionEvent>,
uninstallEvent: Emitter<IExtensionIdentifier>,
didUninstallEvent: Emitter<DidUninstallExtensionEvent>;
suiteSetup(() => {
instantiationService = new TestInstantiationService();
installEvent = new Emitter<InstallExtensionEvent>();
didInstallEvent = new Emitter<DidInstallExtensionEvent>();
uninstallEvent = new Emitter<IExtensionIdentifier>();
didUninstallEvent = new Emitter<DidUninstallExtensionEvent>();
instantiationService.stub(IExtensionManagementService, ExtensionManagementService);
instantiationService.stub(IExtensionManagementService, 'onInstallExtension', installEvent.event);
instantiationService.stub(IExtensionManagementService, 'onDidInstallExtension', didInstallEvent.event);
instantiationService.stub(IExtensionManagementService, 'onUninstallExtension', uninstallEvent.event);
instantiationService.stub(IExtensionManagementService, 'onDidUninstallExtension', didUninstallEvent.event);
instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService));
instantiationService.stub(ITelemetryService, NullTelemetryService);
instantiationService.stub(IURLService, URLService);
instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [local]);
testConfigurationService = new TestConfigurationService();
instantiationService.stub(IConfigurationService, testConfigurationService);
instantiationService.stub(ILifecycleService, new TestLifecycleService());
instantiationService.stub(IStorageService, { get: (a, b, c) => c, getBoolean: (a, b, c) => c, store: () => { }, remove: () => { } });
setup(() => {
instantiationService.stub(IEnvironmentService, {});
instantiationService.stub(IStorageService, { get: (a, b, c) => c, getBoolean: (a, b, c) => c, store: () => { }, remove: () => { } });
});
teardown(() => {
if (testObject) {
testObject.dispose();
}
});
});
test('Simple Experiment Test', () => {
experimentData = {
experiments: [
{
id: 'experiment1'
},
{
id: 'experiment2',
enabled: false
},
{
id: 'experiment3',
enabled: true
},
{
id: 'experiment4',
enabled: true,
condition: {
}
},
{
id: 'experiment5',
enabled: true,
condition: {
insidersOnly: true
}
}
]
};
testObject = instantiationService.createInstance(TestExperimentService);
const tests: Promise<IExperiment>[] = [];
tests.push(testObject.getExperimentById('experiment1'));
tests.push(testObject.getExperimentById('experiment2'));
tests.push(testObject.getExperimentById('experiment3'));
tests.push(testObject.getExperimentById('experiment4'));
tests.push(testObject.getExperimentById('experiment5'));
return Promise.all(tests).then(results => {
assert.equal(results[0].id, 'experiment1');
assert.equal(results[0].enabled, false);
assert.equal(results[0].state, ExperimentState.NoRun);
assert.equal(results[1].id, 'experiment2');
assert.equal(results[1].enabled, false);
assert.equal(results[1].state, ExperimentState.NoRun);
assert.equal(results[2].id, 'experiment3');
assert.equal(results[2].enabled, true);
assert.equal(results[2].state, ExperimentState.Run);
assert.equal(results[3].id, 'experiment4');
assert.equal(results[3].enabled, true);
assert.equal(results[3].state, ExperimentState.Run);
assert.equal(results[4].id, 'experiment5');
assert.equal(results[4].enabled, true);
assert.equal(results[4].state, ExperimentState.Run);
});
});
test('Insiders only experiment shouldnt be enabled in stable', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {
insidersOnly: true
}
}
]
};
instantiationService.stub(IEnvironmentService, { appQuality: 'stable' });
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.NoRun);
});
});
test('NewUsers experiment shouldnt be enabled for old users', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {
newUser: true
}
}
]
};
instantiationService.stub(IStorageService, {
get: (a, b, c) => {
return a === lastSessionDateStorageKey ? 'some-date' : undefined;
},
getBoolean: (a, b, c) => c, store: () => { }, remove: () => { }
});
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.NoRun);
});
});
test('OldUsers experiment shouldnt be enabled for new users', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {
newUser: false
}
}
]
};
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.NoRun);
});
});
test('Experiment without NewUser condition should be enabled for old users', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {}
}
]
};
instantiationService.stub(IStorageService, {
get: (a, b, c) => {
return a === lastSessionDateStorageKey ? 'some-date' : undefined;
},
getBoolean: (a, b, c) => c, store: () => { }, remove: () => { }
});
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.Run);
});
});
test('Experiment without NewUser condition should be enabled for new users', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {}
}
]
};
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.Run);
});
});
test('Experiment with no matching display language should be disabled', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {
displayLanguage: 'somethingthat-nooneknows'
}
}
]
};
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.NoRun);
});
});
test('Experiment with condition type InstalledExtensions is enabled when one of the expected extensions is installed', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {
installedExtensions: {
inlcudes: ['pub.installedExtension1', 'uninstalled-extention-id']
}
}
}
]
};
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.Run);
});
});
test('Experiment with condition type InstalledExtensions is disabled when none of the expected extensions is installed', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {
installedExtensions: {
includes: ['uninstalled-extention-id1', 'uninstalled-extention-id2']
}
}
}
]
};
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.NoRun);
});
});
test('Experiment with condition type InstalledExtensions is disabled when one of the exlcuded extensions is installed', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {
installedExtensions: {
excludes: ['pub.installedExtension1', 'uninstalled-extention-id2']
}
}
}
]
};
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.NoRun);
});
});
test('Experiment that is marked as complete should be disabled regardless of the conditions', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {
installedExtensions: {
includes: ['pub.installedExtension1', 'uninstalled-extention-id2']
}
}
}
]
};
instantiationService.stub(IStorageService, {
get: (a, b, c) => a === 'experiments.experiment1' ? JSON.stringify({ state: ExperimentState.Complete }) : c,
store: (a, b, c) => { }
});
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.Complete);
});
});
test('Experiment with evaluate only once should read enablement from storage service', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
condition: {
installedExtensions: {
excludes: ['pub.installedExtension1', 'uninstalled-extention-id2']
},
evaluateOnlyOnce: true
}
}
]
};
instantiationService.stub(IStorageService, {
get: (a, b, c) => a === 'experiments.experiment1' ? JSON.stringify({ enabled: true, state: ExperimentState.Run }) : c,
store: (a, b, c) => { }
});
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.Run);
});
});
test('Curated list should be available if experiment is enabled.', () => {
const promptText = 'Hello there! Can you see this?';
const curatedExtensionsKey = 'AzureDeploy';
const curatedExtensionsList = ['uninstalled-extention-id1', 'uninstalled-extention-id2'];
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: true,
action: {
type: 'Prompt',
properties: {
promptText,
commands: [
{
text: 'Search Marketplace',
dontShowAgain: true,
curatedExtensionsKey,
curatedExtensionsList
},
{
text: 'No'
}
]
}
}
}
]
};
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, true);
assert.equal(result.state, ExperimentState.Run);
return testObject.getCuratedExtensionsList(curatedExtensionsKey).then(curatedList => {
assert.equal(curatedList, curatedExtensionsList);
});
});
});
test('Curated list shouldnt be available if experiment is disabled.', () => {
const promptText = 'Hello there! Can you see this?';
const curatedExtensionsKey = 'AzureDeploy';
const curatedExtensionsList = ['uninstalled-extention-id1', 'uninstalled-extention-id2'];
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: false,
action: {
type: 'Prompt',
properties: {
promptText,
commands: [
{
text: 'Search Marketplace',
dontShowAgain: true,
curatedExtensionsKey,
curatedExtensionsList
},
{
text: 'No'
}
]
}
}
}
]
};
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, false);
assert.equal(result.state, ExperimentState.NoRun);
return testObject.getCuratedExtensionsList(curatedExtensionsKey).then(curatedList => {
assert.equal(curatedList.length, 0);
});
});
});
test('Experiment that is disabled or deleted should be removed from storage', () => {
experimentData = {
experiments: [
{
id: 'experiment1',
enabled: false
},
{
id: 'experiment3',
enabled: true
}
]
};
let storageDataExperiment1: ExperimentSettings | null = { enabled: false };
let storageDataExperiment2: ExperimentSettings | null = { enabled: false };
let storageDataAllExperiments: string[] | null = ['experiment1', 'experiment2', 'experiment3'];
instantiationService.stub(IStorageService, {
get: (a, b, c) => {
switch (a) {
case 'experiments.experiment1':
return JSON.stringify(storageDataExperiment1);
case 'experiments.experiment2':
return JSON.stringify(storageDataExperiment2);
case 'allExperiments':
return JSON.stringify(storageDataAllExperiments);
default:
break;
}
return c;
},
store: (a, b, c) => {
switch (a) {
case 'experiments.experiment1':
storageDataExperiment1 = JSON.parse(b);
break;
case 'experiments.experiment2':
storageDataExperiment2 = JSON.parse(b);
break;
case 'allExperiments':
storageDataAllExperiments = JSON.parse(b);
break;
default:
break;
}
},
remove: a => {
switch (a) {
case 'experiments.experiment1':
storageDataExperiment1 = null;
break;
case 'experiments.experiment2':
storageDataExperiment2 = null;
break;
case 'allExperiments':
storageDataAllExperiments = null;
break;
default:
break;
}
}
});
testObject = instantiationService.createInstance(TestExperimentService);
const disabledExperiment = testObject.getExperimentById('experiment1').then(result => {
assert.equal(result.enabled, false);
assert.equal(!!storageDataExperiment1, false);
});
const deletedExperiment = testObject.getExperimentById('experiment2').then(result => {
assert.equal(!!result, false);
assert.equal(!!storageDataExperiment2, false);
});
return Promise.all([disabledExperiment, deletedExperiment]).then(() => {
assert.equal(storageDataAllExperiments!.length, 1);
assert.equal(storageDataAllExperiments![0], 'experiment3');
});
});
test('Offline mode', () => {
experimentData = {
experiments: null
};
let storageDataExperiment1: ExperimentSettings | null = { enabled: true, state: ExperimentState.Run };
let storageDataExperiment2: ExperimentSettings | null = { enabled: true, state: ExperimentState.NoRun };
let storageDataExperiment3: ExperimentSettings | null = { enabled: true, state: ExperimentState.Evaluating };
let storageDataExperiment4: ExperimentSettings | null = { enabled: true, state: ExperimentState.Complete };
let storageDataAllExperiments: string[] | null = ['experiment1', 'experiment2', 'experiment3', 'experiment4'];
instantiationService.stub(IStorageService, {
get: (a, b, c) => {
switch (a) {
case 'experiments.experiment1':
return JSON.stringify(storageDataExperiment1);
case 'experiments.experiment2':
return JSON.stringify(storageDataExperiment2);
case 'experiments.experiment3':
return JSON.stringify(storageDataExperiment3);
case 'experiments.experiment4':
return JSON.stringify(storageDataExperiment4);
case 'allExperiments':
return JSON.stringify(storageDataAllExperiments);
default:
break;
}
return c;
},
store: (a, b, c) => {
switch (a) {
case 'experiments.experiment1':
storageDataExperiment1 = JSON.parse(b);
break;
case 'experiments.experiment2':
storageDataExperiment2 = JSON.parse(b);
break;
case 'experiments.experiment3':
storageDataExperiment3 = JSON.parse(b);
break;
case 'experiments.experiment4':
storageDataExperiment4 = JSON.parse(b);
break;
case 'allExperiments':
storageDataAllExperiments = JSON.parse(b);
break;
default:
break;
}
},
remove: a => {
switch (a) {
case 'experiments.experiment1':
storageDataExperiment1 = null;
break;
case 'experiments.experiment2':
storageDataExperiment2 = null;
break;
case 'experiments.experiment3':
storageDataExperiment3 = null;
break;
case 'experiments.experiment4':
storageDataExperiment4 = null;
break;
case 'allExperiments':
storageDataAllExperiments = null;
break;
default:
break;
}
}
});
testObject = instantiationService.createInstance(TestExperimentService);
const tests: Promise<IExperiment>[] = [];
tests.push(testObject.getExperimentById('experiment1'));
tests.push(testObject.getExperimentById('experiment2'));
tests.push(testObject.getExperimentById('experiment3'));
tests.push(testObject.getExperimentById('experiment4'));
return Promise.all(tests).then(results => {
assert.equal(results[0].id, 'experiment1');
assert.equal(results[0].enabled, true);
assert.equal(results[0].state, ExperimentState.Run);
assert.equal(results[1].id, 'experiment2');
assert.equal(results[1].enabled, true);
assert.equal(results[1].state, ExperimentState.NoRun);
assert.equal(results[2].id, 'experiment3');
assert.equal(results[2].enabled, true);
assert.equal(results[2].state, ExperimentState.Evaluating);
assert.equal(results[3].id, 'experiment4');
assert.equal(results[3].enabled, true);
assert.equal(results[3].state, ExperimentState.Complete);
});
});
test('getExperimentByType', () => {
const customProperties = {
some: 'random-value'
};
experimentData = {
experiments: [
{
id: 'simple-experiment',
enabled: true
},
{
id: 'custom-experiment',
enabled: true,
action: {
type: 'Custom',
properties: customProperties
}
},
{
id: 'custom-experiment-no-properties',
enabled: true,
action: {
type: 'Custom'
}
},
{
id: 'prompt-with-no-commands',
enabled: true,
action: {
type: 'Prompt',
properties: {
promptText: 'someText'
}
}
},
{
id: 'prompt-with-commands',
enabled: true,
action: {
type: 'Prompt',
properties: {
promptText: 'someText',
commands: [
{
text: 'Hello'
}
]
}
}
}
]
};
testObject = instantiationService.createInstance(TestExperimentService);
const custom = testObject.getExperimentsByType(ExperimentActionType.Custom).then(result => {
assert.equal(result.length, 3);
assert.equal(result[0].id, 'simple-experiment');
assert.equal(result[1].id, 'custom-experiment');
assert.equal(result[1].action!.properties, customProperties);
assert.equal(result[2].id, 'custom-experiment-no-properties');
assert.equal(!!result[2].action!.properties, true);
});
const prompt = testObject.getExperimentsByType(ExperimentActionType.Prompt).then(result => {
assert.equal(result.length, 2);
assert.equal(result[0].id, 'prompt-with-no-commands');
assert.equal(result[1].id, 'prompt-with-commands');
});
return Promise.all([custom, prompt]);
});
test('experimentsPreviouslyRun includes, excludes check', () => {
experimentData = {
experiments: [
{
id: 'experiment3',
enabled: true,
condition: {
experimentsPreviouslyRun: {
includes: ['experiment1'],
excludes: ['experiment2']
}
}
},
{
id: 'experiment4',
enabled: true,
condition: {
experimentsPreviouslyRun: {
includes: ['experiment1'],
excludes: ['experiment200']
}
}
}
]
};
let storageDataExperiment3 = { enabled: true, state: ExperimentState.Evaluating };
let storageDataExperiment4 = { enabled: true, state: ExperimentState.Evaluating };
instantiationService.stub(IStorageService, {
get: (a, b, c) => {
switch (a) {
case 'currentOrPreviouslyRunExperiments':
return JSON.stringify(['experiment1', 'experiment2']);
default:
break;
}
return c;
},
store: (a, b, c) => {
switch (a) {
case 'experiments.experiment3':
storageDataExperiment3 = JSON.parse(b);
break;
case 'experiments.experiment4':
storageDataExperiment4 = JSON.parse(b);
break;
default:
break;
}
}
});
testObject = instantiationService.createInstance(TestExperimentService);
return testObject.getExperimentsByType(ExperimentActionType.Custom).then(result => {
assert.equal(result.length, 2);
assert.equal(result[0].id, 'experiment3');
assert.equal(result[0].state, ExperimentState.NoRun);
assert.equal(result[1].id, 'experiment4');
assert.equal(result[1].state, ExperimentState.Run);
assert.equal(storageDataExperiment3.state, ExperimentState.NoRun);
assert.equal(storageDataExperiment4.state, ExperimentState.Run);
return Promise.resolve(null);
});
});
// test('Experiment with condition type FileEdit should increment editcount as appropriate', () => {
// });
// test('Experiment with condition type WorkspaceEdit should increment editcount as appropriate', () => {
// });
});

View File

@@ -0,0 +1,194 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Emitter } from 'vs/base/common/event';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { ExperimentalPrompts } from 'vs/workbench/contrib/experiments/electron-browser/experimentalPrompt';
import { ExperimentActionType, ExperimentState, IExperiment, IExperimentActionPromptProperties, IExperimentService } from 'vs/workbench/contrib/experiments/node/experimentService';
import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/electron-browser/experimentService.test';
import { TestLifecycleService } from 'vs/workbench/test/workbenchTestServices';
suite('Experimental Prompts', () => {
let instantiationService: TestInstantiationService;
let experimentService: TestExperimentService;
let experimentalPrompt: ExperimentalPrompts;
let onExperimentEnabledEvent: Emitter<IExperiment>;
let storageData = {};
const promptText = 'Hello there! Can you see this?';
const experiment: IExperiment =
{
id: 'experiment1',
enabled: true,
state: ExperimentState.Run,
action: {
type: ExperimentActionType.Prompt,
properties: {
promptText,
commands: [
{
text: 'Yes',
externalLink: 'https://code.visualstudio.com'
},
{
text: 'No'
}
]
}
}
};
suiteSetup(() => {
instantiationService = new TestInstantiationService();
instantiationService.stub(ILifecycleService, new TestLifecycleService());
instantiationService.stub(ITelemetryService, NullTelemetryService);
onExperimentEnabledEvent = new Emitter<IExperiment>();
});
setup(() => {
storageData = {};
instantiationService.stub(IStorageService, {
get: (a, b, c) => a === 'experiments.experiment1' ? JSON.stringify(storageData) : c,
store: (a, b, c) => {
if (a === 'experiments.experiment1') {
storageData = JSON.parse(b);
}
}
});
instantiationService.stub(INotificationService, new TestNotificationService());
experimentService = instantiationService.createInstance(TestExperimentService);
experimentService.onExperimentEnabled = onExperimentEnabledEvent.event;
instantiationService.stub(IExperimentService, experimentService);
});
teardown(() => {
if (experimentService) {
experimentService.dispose();
}
if (experimentalPrompt) {
experimentalPrompt.dispose();
}
});
test('Show experimental prompt if experiment should be run. Choosing option with link should mark experiment as complete', () => {
storageData = {
enabled: true,
state: ExperimentState.Run
};
instantiationService.stub(INotificationService, {
prompt: (a: Severity, b: string, c: IPromptChoice[], options) => {
assert.equal(b, promptText);
assert.equal(c.length, 2);
c[0].run();
}
});
experimentalPrompt = instantiationService.createInstance(ExperimentalPrompts);
onExperimentEnabledEvent.fire(experiment);
return Promise.resolve(null).then(result => {
assert.equal(storageData['state'], ExperimentState.Complete);
});
});
test('Show experimental prompt if experiment should be run. Choosing negative option should mark experiment as complete', () => {
storageData = {
enabled: true,
state: ExperimentState.Run
};
instantiationService.stub(INotificationService, {
prompt: (a: Severity, b: string, c: IPromptChoice[], options) => {
assert.equal(b, promptText);
assert.equal(c.length, 2);
c[1].run();
}
});
experimentalPrompt = instantiationService.createInstance(ExperimentalPrompts);
onExperimentEnabledEvent.fire(experiment);
return Promise.resolve(null).then(result => {
assert.equal(storageData['state'], ExperimentState.Complete);
});
});
test('Show experimental prompt if experiment should be run. Cancelling should mark experiment as complete', () => {
storageData = {
enabled: true,
state: ExperimentState.Run
};
instantiationService.stub(INotificationService, {
prompt: (a: Severity, b: string, c: IPromptChoice[], options) => {
assert.equal(b, promptText);
assert.equal(c.length, 2);
options.onCancel();
}
});
experimentalPrompt = instantiationService.createInstance(ExperimentalPrompts);
onExperimentEnabledEvent.fire(experiment);
return Promise.resolve(null).then(result => {
assert.equal(storageData['state'], ExperimentState.Complete);
});
});
test('Test getPromptText', () => {
const simpleTextCase: IExperimentActionPromptProperties = {
promptText: 'My simple prompt',
commands: []
};
const multipleLocaleCase: IExperimentActionPromptProperties = {
promptText: {
en: 'My simple prompt for en',
de: 'My simple prompt for de',
'en-au': 'My simple prompt for Austrailian English',
'en-us': 'My simple prompt for US English'
},
commands: []
};
const englishUSTextCase: IExperimentActionPromptProperties = {
promptText: {
'en-us': 'My simple prompt for en'
},
commands: []
};
const noEnglishTextCase: IExperimentActionPromptProperties = {
promptText: {
'de-de': 'My simple prompt for German'
},
commands: []
};
assert.equal(ExperimentalPrompts.getLocalizedText(simpleTextCase.promptText, 'any-language'), simpleTextCase.promptText);
assert.equal(ExperimentalPrompts.getLocalizedText(multipleLocaleCase.promptText, 'en'), multipleLocaleCase.promptText['en']);
assert.equal(ExperimentalPrompts.getLocalizedText(multipleLocaleCase.promptText, 'de'), multipleLocaleCase.promptText['de']);
assert.equal(ExperimentalPrompts.getLocalizedText(multipleLocaleCase.promptText, 'en-au'), multipleLocaleCase.promptText['en-au']);
assert.equal(ExperimentalPrompts.getLocalizedText(multipleLocaleCase.promptText, 'en-gb'), multipleLocaleCase.promptText['en']);
assert.equal(ExperimentalPrompts.getLocalizedText(multipleLocaleCase.promptText, 'fr'), multipleLocaleCase.promptText['en']);
assert.equal(ExperimentalPrompts.getLocalizedText(englishUSTextCase.promptText, 'fr'), englishUSTextCase.promptText['en-us']);
assert.equal(!!ExperimentalPrompts.getLocalizedText(noEnglishTextCase.promptText, 'fr'), false);
});
});