CHE-10538: improves workspace loader (#10642)
* CHE-10538: update workspace loading flow Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com> * update licenses Signed-off-by: Oleksii Kurinnyi <okurinny@redhat.com> * fixup! CHE-10538: update workspace loading flow6.19.x
parent
0a349dbc4c
commit
b03be67f04
|
|
@ -1,8 +1,9 @@
|
|||
# Copyright (c) 2018-2018 Red Hat, Inc
|
||||
# All rights reserved. This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v1.0
|
||||
# which accompanies this distribution, and is available at
|
||||
# http://www.eclipse.org/legal/epl-v10.html
|
||||
# Copyright (c) 2018-2018 Red Hat, Inc.
|
||||
# This program and the accompanying materials
|
||||
# are made available under the terms of the Eclipse Public License v2.0
|
||||
# which is available at http://www.eclipse.org/legal/epl-2.0.html
|
||||
#
|
||||
# SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
# This is a Dockerfile allowing to build workspace loader by using a docker container.
|
||||
# Build step: $ docker build -t eclipse-che-workspace-loader .
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
<!--
|
||||
Copyright (c) 2018-2018 Red Hat, Inc.
|
||||
All rights reserved. This program and the accompanying materials
|
||||
are made available under the terms of the Eclipse Public License v1.0
|
||||
which accompanies this distribution, and is available at
|
||||
http://www.eclipse.org/legal/epl-v10.html
|
||||
This program and the accompanying materials
|
||||
are made available under the terms of the Eclipse Public License v2.0
|
||||
which is available at http://www.eclipse.org/legal/epl-2.0.html
|
||||
|
||||
SPDX-License-Identifier: EPL-2.0
|
||||
|
||||
Contributors:
|
||||
Red Hat, Inc. - initial API and implementation
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
<div id="workspace-loader-progress-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="workspace-loader-reload">Press F5 or click <a href="#">here</a> to try again.</div>
|
||||
</div>
|
||||
<div id="workspace-console">
|
||||
<div id="workspace-console-container"></div>
|
||||
|
|
|
|||
|
|
@ -140,34 +140,15 @@ export class WorkspaceLoader {
|
|||
this.workspace = workspace;
|
||||
return this.handleWorkspace();
|
||||
})
|
||||
.then(() => this.openIDE())
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
this.loader.error(err);
|
||||
this.loader.hideLoader();
|
||||
this.loader.showReload();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the workspace has preconfigured IDE.
|
||||
*/
|
||||
hasPreconfiguredIDE() : boolean {
|
||||
if (this.workspace.config.defaultEnv && this.workspace.config.environments) {
|
||||
let defaultEnvironment = this.workspace.config.defaultEnv;
|
||||
let environment = this.workspace.config.environments[defaultEnvironment];
|
||||
|
||||
let machines = environment.machines;
|
||||
for (let machineName in machines) {
|
||||
let servers = machines[machineName].servers;
|
||||
for (let serverName in servers) {
|
||||
let attributes = servers[serverName].attributes;
|
||||
if (attributes['type'] === 'ide') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns workspace key from current address or empty string when it is undefined.
|
||||
*/
|
||||
|
|
@ -205,12 +186,30 @@ export class WorkspaceLoader {
|
|||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState !== 4) { return; }
|
||||
if (xhr.status !== 200) {
|
||||
reject(xhr.status ? xhr.statusText : "Unknown error");
|
||||
let response;
|
||||
try {
|
||||
response = JSON.parse(xhr.responseText);
|
||||
} catch (e) {}
|
||||
|
||||
if (response) {
|
||||
reject(response);
|
||||
} else if (xhr.statusText) {
|
||||
reject(xhr.statusText);
|
||||
} else {
|
||||
reject("Unknown error");
|
||||
}
|
||||
return;
|
||||
}
|
||||
resolve(JSON.parse(xhr.responseText));
|
||||
};
|
||||
});
|
||||
}).catch((error: any) => {
|
||||
let errorMessage = '';
|
||||
if (error) {
|
||||
errorMessage = error.message ? error.message.toString() : error.toString();
|
||||
}
|
||||
errorMessage = `Failed to get the workspace` + (errorMessage ? `: "${errorMessage}"` : '.');
|
||||
return Promise.reject(errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -226,12 +225,30 @@ export class WorkspaceLoader {
|
|||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState !== 4) { return; }
|
||||
if (xhr.status !== 200) {
|
||||
reject(xhr.status ? xhr.statusText : "Unknown error");
|
||||
let response;
|
||||
try {
|
||||
response = JSON.parse(xhr.responseText);
|
||||
} catch (e) {}
|
||||
|
||||
if (response) {
|
||||
reject(response);
|
||||
} else if (xhr.statusText) {
|
||||
reject(xhr.statusText);
|
||||
} else {
|
||||
reject("Unknown error");
|
||||
}
|
||||
return;
|
||||
}
|
||||
resolve(JSON.parse(xhr.responseText));
|
||||
};
|
||||
});
|
||||
}).catch((error: any) => {
|
||||
let errorMessage = '';
|
||||
if (error) {
|
||||
errorMessage = error.message ? error.message.toString() : error.toString();
|
||||
}
|
||||
errorMessage = `Failed to start the workspace` + (errorMessage ? `: "${errorMessage}"` : '.');
|
||||
return Promise.reject(errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -240,17 +257,40 @@ export class WorkspaceLoader {
|
|||
*/
|
||||
handleWorkspace(): Promise<void> {
|
||||
if (this.workspace.status === 'RUNNING') {
|
||||
this.openIDE();
|
||||
return;
|
||||
return Promise.resolve();
|
||||
} else if (this.workspace.status === 'STOPPING') {
|
||||
this.startAfterStopping = true;
|
||||
}
|
||||
|
||||
return this.subscribeWorkspaceEvents().then(() => {
|
||||
const masterApiConnectionPromise = new Promise((resolve, reject) => {
|
||||
if (this.workspace.status === 'STOPPED') {
|
||||
this.startWorkspace();
|
||||
} else if (this.workspace.status === 'STOPPING') {
|
||||
this.startAfterStopping = true;
|
||||
this.startWorkspace().then(resolve, reject);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}).then(() => {
|
||||
return this.connectMasterApi();
|
||||
});
|
||||
|
||||
const runningOnConnectionPromise = masterApiConnectionPromise
|
||||
.then((masterApi: CheJsonRpcMasterApi) => {
|
||||
return new Promise((resolve) => {
|
||||
masterApi.addListener('open', () => {
|
||||
this.getWorkspace(this.workspace.id).then((workspace) => {
|
||||
if (workspace.status === 'RUNNING') {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const runningOnStatusChangePromise = masterApiConnectionPromise
|
||||
.then((masterApi: CheJsonRpcMasterApi) => {
|
||||
return this.subscribeWorkspaceEvents(masterApi);
|
||||
});
|
||||
|
||||
return Promise.race([runningOnConnectionPromise, runningOnStatusChangePromise]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -262,47 +302,33 @@ export class WorkspaceLoader {
|
|||
this.loader.log(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles changing of workspace status.
|
||||
*
|
||||
* @param status workspace status
|
||||
*/
|
||||
onWorkspaceStatusChanged(status) : void {
|
||||
if (status === 'RUNNING') {
|
||||
this.openIDE();
|
||||
} else if (status === 'STOPPED' && this.startAfterStopping) {
|
||||
this.startWorkspace();
|
||||
}
|
||||
connectMasterApi(): Promise<CheJsonRpcMasterApi> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const entryPoint = this.websocketBaseURL() + WEBSOCKET_CONTEXT + this.getAuthenticationToken();
|
||||
const master = new CheJsonRpcMasterApi(new WebsocketClient(), entryPoint);
|
||||
master.connect(entryPoint)
|
||||
.then(() => resolve(master))
|
||||
.catch((error: any) => reject(error));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to the workspace events.
|
||||
*/
|
||||
subscribeWorkspaceEvents() : Promise<void> {
|
||||
const websocketClient = new WebsocketClient();
|
||||
websocketClient.addListener('open', () => {
|
||||
this.getWorkspace(this.workspace.id).then((workspace) => {
|
||||
this.onWorkspaceStatusChanged(workspace.status ? workspace.status : '');
|
||||
});
|
||||
});
|
||||
const entryPoint = this.websocketBaseURL() + WEBSOCKET_CONTEXT + this.getAuthenticationToken();
|
||||
const master = new CheJsonRpcMasterApi(websocketClient, entryPoint);
|
||||
return new Promise((resolve) => {
|
||||
master.connect(entryPoint).then(() => {
|
||||
master.subscribeEnvironmentOutput(this.workspace.id,
|
||||
(message: any) => this.onEnvironmentOutput(message.text));
|
||||
|
||||
master.subscribeWorkspaceStatus(this.workspace.id,
|
||||
(message: any) => {
|
||||
subscribeWorkspaceEvents(masterApi: CheJsonRpcMasterApi) : Promise<any> {
|
||||
return new Promise((resolve, reject) => {
|
||||
masterApi.subscribeEnvironmentOutput(this.workspace.id,
|
||||
(message: any) => this.onEnvironmentOutput(message.text));
|
||||
masterApi.subscribeWorkspaceStatus(this.workspace.id,
|
||||
(message: any) => {
|
||||
if (message.error) {
|
||||
this.loader.error(message.error);
|
||||
} else {
|
||||
this.onWorkspaceStatusChanged(message.status);
|
||||
} else if (message.status === 'RUNNING') {
|
||||
resolve();
|
||||
} else if (message.status === 'STOPPED') {
|
||||
this.startWorkspace().catch((error: any) => reject(error));
|
||||
}
|
||||
});
|
||||
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
*/
|
||||
'use strict';
|
||||
import {CheJsonRpcApiClient} from './che-json-rpc-api-service';
|
||||
import { ICommunicationClient, CODE_REQUEST_TIMEOUT } from './json-rpc-client';
|
||||
import { ICommunicationClient, CODE_REQUEST_TIMEOUT, CommunicationClientEvent } from './json-rpc-client';
|
||||
|
||||
enum MasterChannels {
|
||||
ENVIRONMENT_OUTPUT = <any>'machine/log',
|
||||
|
|
@ -53,6 +53,14 @@ export class CheJsonRpcMasterApi {
|
|||
});
|
||||
}
|
||||
|
||||
addListener(eventType: CommunicationClientEvent, handler: Function): void {
|
||||
this.client.addListener(eventType, handler);
|
||||
}
|
||||
|
||||
removeListener(eventType: CommunicationClientEvent, handler: Function): void {
|
||||
this.client.removeListener(eventType, handler);
|
||||
}
|
||||
|
||||
onConnectionOpen(): void {
|
||||
if (this.checkingInterval) {
|
||||
clearInterval(this.checkingInterval);
|
||||
|
|
@ -113,8 +121,6 @@ export class CheJsonRpcMasterApi {
|
|||
}
|
||||
return this.cheJsonRpcApi.connect(entryPoint).then(() => {
|
||||
return this.fetchClientId();
|
||||
}).catch((error: any) => {
|
||||
console.error(`Failed to connect to ${entryPoint}:`, error);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,18 @@ export class Loader {
|
|||
}, 1);
|
||||
|
||||
/** Add click handler to maximize output */
|
||||
document.getElementById('workspace-console').onclick = () => this.onclick();
|
||||
document.getElementById('workspace-console').onclick = () => this.onclickConsole();
|
||||
|
||||
document.getElementById('workspace-loader-reload').onclick = () => this.onclickReload();
|
||||
}
|
||||
|
||||
hideLoader(): void {
|
||||
document.getElementById('workspace-loader-label').style.display = 'none';
|
||||
document.getElementById('workspace-loader-progress').style.display = 'none';
|
||||
}
|
||||
|
||||
showReload(): void {
|
||||
document.getElementById('workspace-loader-reload').style.display = 'block';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -59,7 +70,7 @@ export class Loader {
|
|||
element.className = "error";
|
||||
}
|
||||
|
||||
onclick(): void {
|
||||
onclickConsole(): void {
|
||||
if (document.getElementById('workspace-loader').hasAttribute("max")) {
|
||||
document.getElementById('workspace-loader').removeAttribute("max");
|
||||
document.getElementById('workspace-console').removeAttribute("max");
|
||||
|
|
@ -69,4 +80,9 @@ export class Loader {
|
|||
}
|
||||
}
|
||||
|
||||
onclickReload(): boolean {
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,24 @@
|
|||
color: #a0a9b7;
|
||||
}
|
||||
|
||||
#workspace-loader-reload {
|
||||
display: none;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 300px;
|
||||
height: 30px;
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
color: #a0a9b7;
|
||||
}
|
||||
|
||||
#workspace-loader-reload a {
|
||||
color: #a0a9b7;
|
||||
}
|
||||
|
||||
#workspace-loader-progress {
|
||||
position: absolute;
|
||||
width: 300px;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* which is available at http://www.eclipse.org/legal/epl-2.0.html
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* which is available at http://www.eclipse.org/legal/epl-2.0.html
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
|
|
|
|||
|
|
@ -1,19 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* which is available at http://www.eclipse.org/legal/epl-2.0.html
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
*/
|
||||
|
||||
/// <reference path="../src/custom.d.ts" />
|
||||
/// <reference path="../src/custom.d.ts" />
|
||||
|
||||
'use strict';
|
||||
|
||||
import {WorkspaceLoader} from '../src/index';
|
||||
import { WorkspaceLoader } from '../src/index';
|
||||
import { Loader } from '../src/loader/loader';
|
||||
|
||||
describe('Workspace Loader', () => {
|
||||
|
|
@ -28,6 +29,7 @@ describe('Workspace Loader', () => {
|
|||
<div id="workspace-loader-progress-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="workspace-loader-reload">Press F5 or click <a href="#">here</a> to try again.</div>
|
||||
</div>
|
||||
<div id="workspace-console">
|
||||
<div id="workspace-console-container"></div>
|
||||
|
|
@ -64,27 +66,27 @@ describe('Workspace Loader', () => {
|
|||
} as che.IWorkspace;
|
||||
});
|
||||
|
||||
it('must have "workspace-loader" in DOM', () => {
|
||||
const loader = document.getElementById('workspace-loader');
|
||||
expect(loader).toBeTruthy();
|
||||
it('should have "workspace-loader" in DOM', () => {
|
||||
const loaderElement = document.getElementById('workspace-loader');
|
||||
expect(loaderElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('test when workspace key is not specified', () => {
|
||||
let loader = new Loader();
|
||||
let workspaceLoader = new WorkspaceLoader(loader);
|
||||
it('should not get a workspace if workspace key is not specified', () => {
|
||||
const loader = new Loader();
|
||||
const workspaceLoader = new WorkspaceLoader(loader);
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspaceKey');
|
||||
spyOn(workspaceLoader, 'getWorkspace');
|
||||
|
||||
|
||||
workspaceLoader.load();
|
||||
|
||||
expect(workspaceLoader.getWorkspaceKey).toHaveBeenCalled();
|
||||
expect(workspaceLoader.getWorkspace).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('test getWorkspace with test value', () => {
|
||||
let loader = new Loader();
|
||||
let workspaceLoader = new WorkspaceLoader(loader);
|
||||
it('should get a workspace by its workspace key', () => {
|
||||
const loader = new Loader();
|
||||
const workspaceLoader = new WorkspaceLoader(loader);
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspaceKey').and.returnValue("foo/bar");
|
||||
|
||||
|
|
@ -100,64 +102,12 @@ describe('Workspace Loader', () => {
|
|||
expect(workspaceLoader.getWorkspace).toHaveBeenCalledWith("foo/bar");
|
||||
});
|
||||
|
||||
describe('must open IDE directly when workspace does not have IDE server', () => {
|
||||
let workspaceLoader;
|
||||
|
||||
beforeEach((done) => {
|
||||
let loader = new Loader();
|
||||
workspaceLoader = new WorkspaceLoader(loader);
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspaceKey').and.returnValue("foo/bar");
|
||||
spyOn(workspaceLoader, 'getQueryString').and.returnValue("");
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspace').and.callFake(() => {
|
||||
return new Promise((resolve) => {
|
||||
fakeWorkspaceConfig.status = 'STOPPED';
|
||||
fakeWorkspaceConfig.config.environments["default"].machines = {};
|
||||
fakeWorkspaceConfig.runtime = {};
|
||||
resolve(fakeWorkspaceConfig);
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "subscribeWorkspaceEvents").and.callFake(() => {
|
||||
return new Promise((resolve) => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "startWorkspace").and.callFake(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "openIDE");
|
||||
|
||||
workspaceLoader.load();
|
||||
});
|
||||
|
||||
it('openIDE must not be called if status is STOPPED', () => {
|
||||
expect(workspaceLoader.openIDE).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('must subscribe to events', () => {
|
||||
expect(workspaceLoader.subscribeWorkspaceEvents).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('must start the workspace', () => {
|
||||
expect(workspaceLoader.startWorkspace).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('must open IDE when workspace become RUNNING', () => {
|
||||
workspaceLoader.onWorkspaceStatusChanged("RUNNING");
|
||||
expect(workspaceLoader.openIDE).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('must open preconfigured IDE with query parameters', () => {
|
||||
describe('if workspace has a preconfigured IDE with query parameters', () => {
|
||||
let ideURL = "ide URL"
|
||||
let workspaceLoader;
|
||||
|
||||
beforeEach((done) => {
|
||||
let loader = new Loader();
|
||||
const loader = new Loader();
|
||||
workspaceLoader = new WorkspaceLoader(loader);
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspaceKey').and.returnValue("foo/bar");
|
||||
|
|
@ -166,63 +116,59 @@ describe('Workspace Loader', () => {
|
|||
spyOn(workspaceLoader, 'getWorkspace').and.callFake(() => {
|
||||
return new Promise((resolve) => {
|
||||
fakeWorkspaceConfig.status = 'RUNNING';
|
||||
fakeWorkspaceConfig.runtime = {machines: {ide: {servers: {server1: {attributes: {type: "ide"}, url: ideURL}}}}};
|
||||
fakeWorkspaceConfig.runtime = { machines: { ide: { servers: { server1: { attributes: { type: "ide" }, url: ideURL } } } } } as any;
|
||||
resolve(fakeWorkspaceConfig);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "openIDE").and.callThrough();
|
||||
spyOn(workspaceLoader, "openURL");
|
||||
workspaceLoader.load();
|
||||
workspaceLoader.load().then(() => done());
|
||||
});
|
||||
|
||||
it('must be called', () => {
|
||||
it('should call openURL method with correct parameter', () => {
|
||||
expect(workspaceLoader.openURL).toHaveBeenCalledWith(ideURL + "?param=value");
|
||||
});
|
||||
});
|
||||
|
||||
describe('must handle workspace when it has IDE server', () => {
|
||||
describe('if workspace does not have an IDE server', () => {
|
||||
let workspaceLoader;
|
||||
|
||||
beforeEach((done) => {
|
||||
let loader = new Loader();
|
||||
const loader = new Loader();
|
||||
workspaceLoader = new WorkspaceLoader(loader);
|
||||
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspaceKey').and.returnValue("foo/bar");
|
||||
|
||||
spyOn(workspaceLoader, 'getQueryString').and.returnValue("");
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspace').and.callFake(() => {
|
||||
return new Promise((resolve) => {
|
||||
fakeWorkspaceConfig.status = 'RUNNING';
|
||||
fakeWorkspaceConfig.config.environments["default"].machines = {};
|
||||
fakeWorkspaceConfig.runtime = {} as che.IWorkspaceRuntime;
|
||||
resolve(fakeWorkspaceConfig);
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "handleWorkspace").and.callFake(() => {
|
||||
done();
|
||||
});
|
||||
|
||||
workspaceLoader.load();
|
||||
spyOn(workspaceLoader, "openIDE").and.callThrough();
|
||||
spyOn(workspaceLoader, "openURL");
|
||||
workspaceLoader.load().then(() => done());
|
||||
});
|
||||
|
||||
it('basic workspace function must be called', () => {
|
||||
expect(workspaceLoader.getWorkspaceKey).toHaveBeenCalled();
|
||||
expect(workspaceLoader.getWorkspace).toHaveBeenCalledWith("foo/bar");
|
||||
});
|
||||
|
||||
it('must be called', () => {
|
||||
expect(workspaceLoader.handleWorkspace).toHaveBeenCalled();
|
||||
it('should open IDE directly', () => {
|
||||
expect(workspaceLoader.openURL).toHaveBeenCalledWith(fakeWorkspaceConfig.links.ide);
|
||||
});
|
||||
});
|
||||
|
||||
describe('must open IDE for RUNNING workspace', () => {
|
||||
let workspaceLoader;
|
||||
describe('if workspace is RUNNING', () => {
|
||||
let workspaceLoader: WorkspaceLoader;
|
||||
|
||||
beforeEach((done) => {
|
||||
let loader = new Loader();
|
||||
const loader = new Loader();
|
||||
workspaceLoader = new WorkspaceLoader(loader);
|
||||
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspaceKey').and.returnValue("foo/bar");
|
||||
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspace').and.callFake(() => {
|
||||
return new Promise((resolve) => {
|
||||
fakeWorkspaceConfig.status = 'RUNNING';
|
||||
|
|
@ -230,33 +176,38 @@ describe('Workspace Loader', () => {
|
|||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "subscribeWorkspaceEvents");
|
||||
|
||||
spyOn(workspaceLoader, "openIDE").and.callFake(() => {
|
||||
done();
|
||||
});
|
||||
spyOn(workspaceLoader, "connectMasterApi");
|
||||
|
||||
workspaceLoader.load();
|
||||
spyOn(workspaceLoader, "subscribeWorkspaceEvents");
|
||||
|
||||
spyOn(workspaceLoader, "openIDE");
|
||||
|
||||
workspaceLoader.load().then(() => done());
|
||||
});
|
||||
|
||||
it('must not subscribe to events', () => {
|
||||
it('should not connect to workspace master API', () => {
|
||||
expect(workspaceLoader.connectMasterApi).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not subscribe to workspace events', () => {
|
||||
expect(workspaceLoader.subscribeWorkspaceEvents).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('must open IDE immediately', () => {
|
||||
|
||||
it('should open IDE immediately', () => {
|
||||
expect(workspaceLoader.openIDE).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('> must start STOPPED workspace', () => {
|
||||
let workspaceLoader;
|
||||
|
||||
describe('if workspace is STOPPED and then starts successfully', () => {
|
||||
let workspaceLoader: WorkspaceLoader;
|
||||
let statusChangeCallback: Function;
|
||||
|
||||
beforeEach((done) => {
|
||||
let loader = new Loader();
|
||||
const loader = new Loader();
|
||||
workspaceLoader = new WorkspaceLoader(loader);
|
||||
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspaceKey').and.returnValue("foo/bar");
|
||||
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspace').and.callFake(() => {
|
||||
return new Promise((resolve) => {
|
||||
fakeWorkspaceConfig.status = 'STOPPED';
|
||||
|
|
@ -264,48 +215,159 @@ describe('Workspace Loader', () => {
|
|||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "subscribeWorkspaceEvents").and.callFake(() => {
|
||||
return new Promise((resolve) => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "subscribeWorkspaceEvents").and.callThrough();
|
||||
|
||||
spyOn(workspaceLoader, "startWorkspace").and.callFake(() => {
|
||||
done();
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "openIDE");
|
||||
spyOn(workspaceLoader, "connectMasterApi").and.callFake(() => {
|
||||
done();
|
||||
return Promise.resolve({
|
||||
addListener: () => { },
|
||||
subscribeEnvironmentOutput: () => { },
|
||||
subscribeWorkspaceStatus: (workspaceId, callback) => {
|
||||
statusChangeCallback = callback;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "openIDE").and.callFake(() => {
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
workspaceLoader.load();
|
||||
});
|
||||
|
||||
it('openIDE must not be called if status is STOPPED', () => {
|
||||
|
||||
it('should not open an IDE', () => {
|
||||
expect(workspaceLoader.openIDE).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('must subscribe to events', () => {
|
||||
|
||||
it('should subscribe to workspace events', () => {
|
||||
expect(workspaceLoader.subscribeWorkspaceEvents).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('must start the workspace', () => {
|
||||
|
||||
it('should start the workspace', () => {
|
||||
expect(workspaceLoader.startWorkspace).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('openIDE must be called when workspace become RUNNING', () => {
|
||||
workspaceLoader.onWorkspaceStatusChanged("RUNNING");
|
||||
expect(workspaceLoader.openIDE).toHaveBeenCalled();
|
||||
describe('then becomes STARTING', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
statusChangeCallback({ status: 'STARTING' });
|
||||
})
|
||||
|
||||
it('should not open an IDE', () => {
|
||||
expect(workspaceLoader.openIDE).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('then becomes RUNNING', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
statusChangeCallback({ status: 'RUNNING' });
|
||||
});
|
||||
|
||||
it('should open an IDE', () => {
|
||||
expect(workspaceLoader.openIDE).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('must restart STOPPING workspace', () => {
|
||||
let workspaceLoader;
|
||||
describe('if workspace is STOPPED and then fails to start', () => {
|
||||
let workspaceLoader: WorkspaceLoader;
|
||||
let startPromiseReject: Function;
|
||||
let workspaceLoadPromise: Promise<void>;
|
||||
|
||||
beforeEach((done) => {
|
||||
let loader = new Loader();
|
||||
const loader = new Loader();
|
||||
workspaceLoader = new WorkspaceLoader(loader);
|
||||
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspaceKey').and.returnValue("foo/bar");
|
||||
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspace').and.callFake(() => {
|
||||
return new Promise((resolve) => {
|
||||
fakeWorkspaceConfig.status = 'STOPPED';
|
||||
resolve(fakeWorkspaceConfig);
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "connectMasterApi").and.callFake(() => {
|
||||
return Promise.resolve({
|
||||
addListener: () => { },
|
||||
subscribeEnvironmentOutput: () => {},
|
||||
subscribeWorkspaceStatus: () => {}
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "subscribeWorkspaceEvents").and.callThrough();
|
||||
|
||||
spyOn(workspaceLoader, "startWorkspace").and.callFake(() => {
|
||||
done();
|
||||
return Promise.reject();
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "openIDE").and.callFake(() => {
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
workspaceLoadPromise = workspaceLoader.load();
|
||||
});
|
||||
|
||||
it('should not open an IDE immediately', () => {
|
||||
expect(workspaceLoader.openIDE).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should start the workspace', () => {
|
||||
expect(workspaceLoader.startWorkspace).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not subscribe to workspace events', () => {
|
||||
expect(workspaceLoader.subscribeWorkspaceEvents).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not open an IDE', () => {
|
||||
expect(workspaceLoader.openIDE).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('then the request for starting the workspace fails', () => {
|
||||
|
||||
beforeEach((done) => {
|
||||
workspaceLoadPromise.then(() => done());
|
||||
});
|
||||
|
||||
it('should hide loader and progress bar', () => {
|
||||
const workspaceLoaderLabel = document.getElementById('workspace-loader-label'),
|
||||
workspaceLoaderProgress = document.getElementById('workspace-loader-progress');
|
||||
|
||||
expect(workspaceLoaderLabel.style.display).toEqual('none');
|
||||
expect(workspaceLoaderProgress.style.display).toEqual('none');
|
||||
});
|
||||
|
||||
it('should show message with "try again" prompt', () => {
|
||||
const workspaceLoaderReload = document.getElementById('workspace-loader-reload');
|
||||
|
||||
expect(workspaceLoaderReload).toBeTruthy();
|
||||
expect(workspaceLoaderReload.style.display).not.toEqual('none');
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('if workspace is STOPPING', () => {
|
||||
let workspaceLoader: WorkspaceLoader;
|
||||
let statusChangeCallback: Function;
|
||||
|
||||
beforeEach((done) => {
|
||||
const loader = new Loader();
|
||||
workspaceLoader = new WorkspaceLoader(loader);
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspaceKey').and.returnValue("foo/bar");
|
||||
|
||||
spyOn(workspaceLoader, 'getWorkspace').and.callFake(() => {
|
||||
return new Promise((resolve) => {
|
||||
fakeWorkspaceConfig.status = 'STOPPING';
|
||||
|
|
@ -313,35 +375,70 @@ describe('Workspace Loader', () => {
|
|||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "subscribeWorkspaceEvents").and.callFake(() => {
|
||||
return new Promise((resolve) => {
|
||||
resolve();
|
||||
spyOn(workspaceLoader, "subscribeWorkspaceEvents").and.callThrough()
|
||||
|
||||
spyOn(workspaceLoader, "startWorkspace").and.callFake(() => {
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "connectMasterApi").and.callFake(() => {
|
||||
done();
|
||||
return Promise.resolve({
|
||||
addListener: () => { },
|
||||
subscribeEnvironmentOutput: () => { },
|
||||
subscribeWorkspaceStatus: (workspaceId, callback) => {
|
||||
statusChangeCallback = callback;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(workspaceLoader, "startWorkspace");
|
||||
spyOn(workspaceLoader, "openIDE");
|
||||
|
||||
workspaceLoader.load().then(() => {
|
||||
done();
|
||||
spyOn(workspaceLoader, "openIDE").and.callFake(() => {
|
||||
return Promise.resolve();
|
||||
});
|
||||
|
||||
workspaceLoader.load();
|
||||
});
|
||||
|
||||
it('must start the workspace after stopping', () => {
|
||||
expect(workspaceLoader.startAfterStopping).toEqual(true);
|
||||
});
|
||||
|
||||
|
||||
it('must start workspace when workspace status become STOPPED', () => {
|
||||
workspaceLoader.onWorkspaceStatusChanged("STOPPED");
|
||||
expect(workspaceLoader.startWorkspace).toHaveBeenCalled();
|
||||
it('should not open an IDE immediately', () => {
|
||||
expect(workspaceLoader.openIDE).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('must open IDE when workspace become RUNNING', () => {
|
||||
workspaceLoader.onWorkspaceStatusChanged("RUNNING");
|
||||
expect(workspaceLoader.openIDE).toHaveBeenCalled();
|
||||
it('should set flag to restart a workspace', () => {
|
||||
expect(workspaceLoader.startAfterStopping).toEqual(true);
|
||||
});
|
||||
|
||||
it('should not start a workspace immediately', () => {
|
||||
expect(workspaceLoader.startWorkspace).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('then becomes STOPPED', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
statusChangeCallback({ status: 'STOPPED' });
|
||||
});
|
||||
|
||||
it('should start a workspace', () => {
|
||||
expect(workspaceLoader.startWorkspace).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not open an IDE', () => {
|
||||
expect(workspaceLoader.openIDE).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('then becomes RUNNING', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
statusChangeCallback({ status: 'RUNNING' });
|
||||
});
|
||||
|
||||
it('should open an IDE', () => {
|
||||
expect(workspaceLoader.openIDE).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2018-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* which is available at http://www.eclipse.org/legal/epl-2.0.html
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2018-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* which is available at http://www.eclipse.org/legal/epl-2.0.html
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2018-2018 Red Hat, Inc.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
* This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v2.0
|
||||
* which is available at http://www.eclipse.org/legal/epl-2.0.html
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*
|
||||
* Contributors:
|
||||
* Red Hat, Inc. - initial API and implementation
|
||||
|
|
|
|||
Loading…
Reference in New Issue