diff --git a/dev-packages/cli/package.json b/dev-packages/cli/package.json index 557b9c7211c2f..1f8f6e5d32509 100644 --- a/dev-packages/cli/package.json +++ b/dev-packages/cli/package.json @@ -48,6 +48,7 @@ "puppeteer": "^2.0.0", "puppeteer-to-istanbul": "^1.2.2", "tar": "^4.0.0", + "temp": "^0.9.1", "unzip-stream": "^0.3.0", "yargs": "^11.1.0" }, diff --git a/dev-packages/cli/src/theia.ts b/dev-packages/cli/src/theia.ts index e734da5f87f68..0da61ffcc7627 100644 --- a/dev-packages/cli/src/theia.ts +++ b/dev-packages/cli/src/theia.ts @@ -14,6 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ +import * as temp from 'temp'; import * as yargs from 'yargs'; import { ApplicationPackageManager, rebuild } from '@theia/application-manager'; import { ApplicationProps } from '@theia/application-package'; @@ -216,6 +217,9 @@ function rebuildCommand(command: string, target: ApplicationProps.Target): yargs testCoverage: boolean }) => { try { + if (!process.env.THEIA_CONFIG_DIR) { + process.env.THEIA_CONFIG_DIR = temp.track().mkdirSync('theia-test-config-dir'); + } await runTest({ start: async () => new Promise((resolve, reject) => { const serverArgs = commandArgs('test').filter(a => a.indexOf('--test-') !== 0); diff --git a/examples/api-tests/src/menus.spec.js b/examples/api-tests/src/menus.spec.js index 7fe4c8f57a400..420af7e1cf259 100644 --- a/examples/api-tests/src/menus.spec.js +++ b/examples/api-tests/src/menus.spec.js @@ -16,6 +16,7 @@ // @ts-check describe('Menus', function () { + this.timeout(7500); const { assert } = chai; @@ -51,8 +52,7 @@ describe('Menus', function () { before(async function () { await pluginService.didStart; - // register views for the explorer view container - await pluginService.activatePlugin('vscode.npm'); + await pluginService.activateByViewContainer('explorer'); }); const toTearDown = new DisposableCollection(); diff --git a/examples/api-tests/src/views.spec.js b/examples/api-tests/src/views.spec.js index d3059ad2435ce..7e494c0cc8be5 100644 --- a/examples/api-tests/src/views.spec.js +++ b/examples/api-tests/src/views.spec.js @@ -16,6 +16,7 @@ // @ts-check describe('Views', function () { + this.timeout(7500); const { assert } = chai; @@ -24,6 +25,7 @@ describe('Views', function () { const { ScmContribution } = require('@theia/scm/lib/browser/scm-contribution'); const { OutlineViewContribution } = require('@theia/outline-view/lib/browser/outline-view-contribution'); const { ProblemContribution } = require('@theia/markers/lib/browser/problem/problem-contribution'); + const { HostedPluginSupport } = require('@theia/plugin-ext/lib/hosted/browser/hosted-plugin'); /** @type {import('inversify').Container} */ const container = window['theia'].container; @@ -32,13 +34,18 @@ describe('Views', function () { const scmContribution = container.get(ScmContribution); const outlineContribution = container.get(OutlineViewContribution); const problemContribution = container.get(ProblemContribution); + const pluginService = container.get(HostedPluginSupport); - before(() => { - shell.leftPanelHandler.collapse(); - }); + before(() => Promise.all([ + shell.leftPanelHandler.collapse(), + (async function () { + await pluginService.didStart; + await pluginService.activateByViewContainer('explorer'); + })() + ])); for (const contribution of [navigatorContribution, scmContribution, outlineContribution, problemContribution]) { - it(`should toggle ${contribution.viewLabel}`, async () => { + it(`should toggle ${contribution.viewLabel}`, async function () { let view = await contribution.closeView(); if (view) { assert.notEqual(shell.getAreaFor(view), contribution.defaultViewOptions.area); diff --git a/packages/core/src/browser/shell/tab-bar-toolbar.tsx b/packages/core/src/browser/shell/tab-bar-toolbar.tsx index 6f2cc21a12124..1732aa6aa48f3 100644 --- a/packages/core/src/browser/shell/tab-bar-toolbar.tsx +++ b/packages/core/src/browser/shell/tab-bar-toolbar.tsx @@ -404,6 +404,9 @@ export class TabBarToolbarRegistry implements FrontendApplicationContribution { * By default returns with all items where the command is enabled and `item.isVisible` is `true`. */ visibleItems(widget: Widget): Array { + if (widget.isDisposed) { + return []; + } const result = []; for (const item of this.items.values()) { const visible = TabBarToolbarItem.is(item) diff --git a/packages/core/src/browser/view-container.ts b/packages/core/src/browser/view-container.ts index 31db6e24674df..e254d92ee8b9a 100644 --- a/packages/core/src/browser/view-container.ts +++ b/packages/core/src/browser/view-container.ts @@ -330,7 +330,11 @@ export class ViewContainer extends BaseWidget implements StatefulWidget, Applica } get containerLayout(): ViewContainerLayout { - return this.panel.layout as ViewContainerLayout; + const layout = this.panel.layout; + if (layout instanceof ViewContainerLayout) { + return layout; + } + throw new Error('view container is disposed'); } protected get orientation(): SplitLayout.Orientation { @@ -1159,7 +1163,7 @@ export class ViewContainerLayout extends SplitLayout { } if (!enableAnimation || this.options.animationDuration <= 0) { - MessageLoop.postMessage(this.parent!, Widget.Msg.FitRequest); + MessageLoop.postMessage(this.parent, Widget.Msg.FitRequest); return; } let startTime: number | undefined = undefined; @@ -1178,6 +1182,10 @@ export class ViewContainerLayout extends SplitLayout { // The update function is called on every animation frame until the predefined duration has elapsed. const updateFunc = (time: number) => { + if (!this.parent) { + part.animatedSize = undefined; + return; + } if (startTime === undefined) { startTime = time; } @@ -1199,11 +1207,13 @@ export class ViewContainerLayout extends SplitLayout { // Request another frame to reset the part to variable size requestAnimationFrame(() => { part.animatedSize = undefined; - MessageLoop.sendMessage(this.parent!, Widget.Msg.FitRequest); + if (this.parent) { + MessageLoop.sendMessage(this.parent, Widget.Msg.FitRequest); + } }); } } - MessageLoop.sendMessage(this.parent!, Widget.Msg.FitRequest); + MessageLoop.sendMessage(this.parent, Widget.Msg.FitRequest); }; requestAnimationFrame(updateFunc); } diff --git a/packages/core/src/node/env-variables/env-variables-server.ts b/packages/core/src/node/env-variables/env-variables-server.ts index a9efe099b5bae..650dd8a703868 100644 --- a/packages/core/src/node/env-variables/env-variables-server.ts +++ b/packages/core/src/node/env-variables/env-variables-server.ts @@ -25,9 +25,11 @@ import { FileUri } from '../file-uri'; export class EnvVariablesServerImpl implements EnvVariablesServer { protected readonly envs: { [key: string]: EnvVariable } = {}; - protected readonly configDirUri = FileUri.create(join(homedir(), '.theia')).toString(); + protected readonly configDirUri: Promise; constructor() { + this.configDirUri = this.createConfigDirUri(); + this.configDirUri.then(configDirUri => console.log(`Configuration directory URI: '${configDirUri}'`)); const prEnv = process.env; Object.keys(prEnv).forEach((key: string) => { let keyName = key; @@ -38,6 +40,10 @@ export class EnvVariablesServerImpl implements EnvVariablesServer { }); } + protected async createConfigDirUri(): Promise { + return FileUri.create(process.env.THEIA_CONFIG_DIR || join(homedir(), '.theia')).toString(); + } + async getExecPath(): Promise { return process.execPath; } @@ -53,7 +59,7 @@ export class EnvVariablesServerImpl implements EnvVariablesServer { return this.envs[key]; } - async getConfigDirUri(): Promise { + getConfigDirUri(): Promise { return this.configDirUri; } diff --git a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts index 885e18d8353c0..8293be0e9949a 100644 --- a/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts +++ b/packages/plugin-ext/src/hosted/browser/hosted-plugin.ts @@ -535,6 +535,10 @@ export class HostedPluginSupport { await Promise.all(activation); } + async activateByViewContainer(viewContainerId: string): Promise { + await Promise.all(this.viewRegistry.getContainerViews(viewContainerId).map(viewId => this.activateByView(viewId))); + } + async activateByView(viewId: string): Promise { await this.activateByEvent(`onView:${viewId}`); } diff --git a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts index 6bb23010440e5..874ae204d25e7 100644 --- a/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts +++ b/packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts @@ -246,6 +246,10 @@ export class PluginViewRegistry implements FrontendApplicationContribution { return toDispose; } + getContainerViews(viewContainerId: string): string[] { + return this.containerViews.get(viewContainerId) || []; + } + registerView(viewContainerId: string, view: View): Disposable { if (this.views.has(view.id)) { console.warn('view with such id already registered: ', JSON.stringify(view)); @@ -256,7 +260,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { this.views.set(view.id, [viewContainerId, view]); toDispose.push(Disposable.create(() => this.views.delete(view.id))); - const containerViews = this.containerViews.get(viewContainerId) || []; + const containerViews = this.getContainerViews(viewContainerId); containerViews.push(view.id); this.containerViews.set(viewContainerId, containerViews); toDispose.push(Disposable.create(() => { @@ -374,7 +378,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution { const [, options] = data; containerWidget.setTitleOptions(options); } - for (const viewId of this.containerViews.get(viewContainerId) || []) { + for (const viewId of this.getContainerViews(viewContainerId)) { const identifier = this.toPluginViewWidgetIdentifier(viewId); const widget = await this.widgetManager.getOrCreateWidget(PLUGIN_VIEW_FACTORY_ID, identifier); if (containerWidget.getTrackableWidgets().indexOf(widget) === -1) { diff --git a/yarn.lock b/yarn.lock index 31b3f597329a5..5f9a27771fa0c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12113,6 +12113,13 @@ temp@^0.8.1, temp@^0.8.3: dependencies: rimraf "~2.6.2" +temp@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/temp/-/temp-0.9.1.tgz#2d666114fafa26966cd4065996d7ceedd4dd4697" + integrity sha512-WMuOgiua1xb5R56lE0eH6ivpVmg/lq2OHm4+LtT/xtEtPQ+sz6N3bBM6WZ5FvO1lO4IKIOb43qnhoc4qxP5OeA== + dependencies: + rimraf "~2.6.2" + tempfile@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-1.1.1.tgz#5bcc4eaecc4ab2c707d8bc11d99ccc9a2cb287f2"