diff --git a/dashboard/src/app/ide/ide.service.ts b/dashboard/src/app/ide/ide.service.ts index 21c0b81729a..4cca34f48bd 100644 --- a/dashboard/src/app/ide/ide.service.ts +++ b/dashboard/src/app/ide/ide.service.ts @@ -159,16 +159,6 @@ class IdeSvc { this.listeningChannels.push(statusChannel); // for now, display log of status channel in case of errors bus.subscribe(statusChannel, (message: any) => { - if (message.eventType === 'DESTROYED' && message.workspaceId === data.id && !(this.$rootScope as any).showIDE) { - // need to show the error - this.$mdDialog.show( - this.$mdDialog.alert() - .title('Unable to start the workspace runtime') - .content('Your workspace runtime is no longer available. It was either destroyed or ran out of memory.') - .ariaLabel('Workspace start') - .ok('OK') - ); - } if (message.eventType === 'ERROR' && message.workspaceId === data.id) { let errorMessage = 'Error when trying to start the workspace'; if (message.error) { diff --git a/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.spec.ts b/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.spec.ts new file mode 100644 index 00000000000..30823359523 --- /dev/null +++ b/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.spec.ts @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2015-2017 Codenvy, S.A. + * 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 + * + * Contributors: + * Codenvy, S.A. - initial API and implementation + */ +'use strict'; +import {CheWorkspace} from '../../../components/api/che-workspace.factory'; +import {CheAPIBuilder} from '../../../components/api/builder/che-api-builder.factory'; +import {CheHttpBackend} from '../../../components/api/test/che-http-backend'; +import IdeSvc from '../../ide/ide.service'; + + +/** + * Test of the NavbarRecentWorkspacesController + */ +describe('NavbarRecentWorkspacesController', () => { + /** + * NavbarRecentWorkspacesController + */ + let navbarRecentWorkspacesController; + + /** + * API builder + */ + let apiBuilder: CheAPIBuilder; + + /** + * Backend for handling http operations + */ + let httpBackend: ng.IHttpBackendService; + + /** + * Che backend + */ + let cheBackend: CheHttpBackend; + + + let workspaces: Array; + + /** + * setup module + */ + beforeEach(angular.mock.module('userDashboard')); + + /** + * Inject factory and http backend + */ + beforeEach(inject(($rootScope: ng.IRootScopeService, cheWorkspace: CheWorkspace, cheAPIBuilder: CheAPIBuilder, cheHttpBackend: CheHttpBackend, $controller: any, ideSvc: IdeSvc, $window: ng.IWindowService, $log: ng.ILogService) => { + apiBuilder = cheAPIBuilder; + cheBackend = cheHttpBackend; + httpBackend = cheHttpBackend.getHttpBackend(); + + let scope = $rootScope.$new(); + navbarRecentWorkspacesController = $controller('NavbarRecentWorkspacesController', { + ideSvc: IdeSvc, cheWorkspace: cheWorkspace, $window: $window, $log: $log, $scope: scope, $rootScope: $rootScope + }); + + workspaces = []; + for (let i = 0; i < 20; ++i) { + let wrkspId = 'workspaceId' + i; + let wrkspName = 'testName' + i; + let wrkspCreateDate = new Date(2001, 1, 1, i, 1).toString(); + let wrkspUpdateDate = new Date(2001, 1, 1, i, 2).toString(); + let wrkspAttr = {'created': Date.parse(wrkspCreateDate), 'updated': Date.parse(wrkspUpdateDate)}; + let workspace = apiBuilder.getWorkspaceBuilder().withId(wrkspId).withAttributes(wrkspAttr).withName(wrkspName).build(); + workspaces.push(workspace); + } + // shuffle the workspaces + workspaces.sort(() => { + return 0.5 - Math.random(); + }); + // providing request + // add workspaces on Http backend + cheBackend.addWorkspaces(workspaces); + + // setup backend + cheBackend.setup(); + + // fetch workspaces + cheWorkspace.fetchWorkspaces(); + + // flush command + httpBackend.flush(); + })); + + /** + * Check assertion after the test + */ + afterEach(() => { + httpBackend.verifyNoOutstandingExpectation(); + httpBackend.verifyNoOutstandingRequest(); + }); + + /** + * Check sorting rule for recent workspaces + */ + it('Check very recent workspaces', inject(() => { + // get recentWorkspaces + let recentWorkspaces = navbarRecentWorkspacesController.getRecentWorkspaces(); + + // check max length + expect(recentWorkspaces.length).toEqual(navbarRecentWorkspacesController.maxItemsNumber); + + // prepare test objects + let testWorkspaces: Array = angular.copy(workspaces); + testWorkspaces.sort((workspace1: che.IWorkspace, workspace2: che.IWorkspace) => { + return workspace2.attributes.updated - workspace1.attributes.updated; + }); + let veryRecentWorkspaceId = testWorkspaces[testWorkspaces.length - 1].id; + + // check default sorting + let lastPosition = recentWorkspaces.length - 1; + for (let i = 0; i < lastPosition; i++) { + expect(recentWorkspaces[i].id).toEqual(testWorkspaces[i].id); + } + // check the last one workspace is equal to the last test workspace and not equal to the very recent workspace, + // because we are going to update very recent workspace in controller and sorting rule should be changed + expect(recentWorkspaces[lastPosition].id).toEqual(testWorkspaces[lastPosition].id); + expect(recentWorkspaces[lastPosition].id).not.toEqual(veryRecentWorkspaceId); + + // update very recent workspace + navbarRecentWorkspacesController.updateRecentWorkspace(veryRecentWorkspaceId); + recentWorkspaces = navbarRecentWorkspacesController.getRecentWorkspaces(); + + // check sorting with veryRecentWorkspace + for (let i = 0; i < lastPosition; i++) { + expect(recentWorkspaces[i].id).toEqual(testWorkspaces[i].id); + } + // check the last one workspace is equal to the very recent workspace and not equal to the last test workspace + expect(recentWorkspaces[lastPosition].id).not.toEqual(testWorkspaces[lastPosition].id); + expect(recentWorkspaces[lastPosition].id).toEqual(veryRecentWorkspaceId); + }) + ); +}); diff --git a/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.ts b/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.ts index 0ef8920cc3e..91ce131df5d 100644 --- a/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.ts +++ b/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.controller.ts @@ -75,6 +75,14 @@ export class NavbarRecentWorkspacesController { this.fetchWorkspaceSettings(); } + /** + * Returns the MAX_RECENT_WORKSPACES_ITEMS constant + * @returns {number} + */ + get maxItemsNumber(): number { + return MAX_RECENT_WORKSPACES_ITEMS; + } + /** * Retrieves workspace settings. */ @@ -84,10 +92,6 @@ export class NavbarRecentWorkspacesController { } else { this.cheWorkspace.fetchWorkspaceSettings().then(() => { this.prepareDropdownItemsTemplate(); - }, (error: any) => { - if (error.status === 304) { - this.prepareDropdownItemsTemplate(); - } }); } } @@ -171,13 +175,10 @@ export class NavbarRecentWorkspacesController { if (recentWorkspaces.length > MAX_RECENT_WORKSPACES_ITEMS) { let pos: number = veryRecentWorkspace ? recentWorkspaces.indexOf(veryRecentWorkspace) : -1; if (veryRecentWorkspace && pos >= MAX_RECENT_WORKSPACES_ITEMS) { - recentWorkspaces.splice(MAX_RECENT_WORKSPACES_ITEMS - 1, recentWorkspaces.length , veryRecentWorkspace); - } else { - recentWorkspaces.splice(0, MAX_RECENT_WORKSPACES_ITEMS); + recentWorkspaces[MAX_RECENT_WORKSPACES_ITEMS - 1] = veryRecentWorkspace; } } - - this.recentWorkspaces = recentWorkspaces; + this.recentWorkspaces = recentWorkspaces.slice(0, MAX_RECENT_WORKSPACES_ITEMS); } /** @@ -272,6 +273,7 @@ export class NavbarRecentWorkspacesController { */ stopRecentWorkspace(workspaceId: string, createSnapshot: boolean): void { this.cheWorkspace.stopWorkspace(workspaceId, createSnapshot).then(() => { + angular.noop(); }, (error: any) => { this.$log.error(error); }); diff --git a/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.html b/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.html index 25d34cc0b33..e302111a51e 100644 --- a/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.html +++ b/dashboard/src/app/navbar/recent-workspaces/recent-workspaces.html @@ -19,7 +19,7 @@ - + -
+
diff --git a/dashboard/src/app/workspaces/workspace-details/environments/list-env-variables/edit-variable-dialog/edit-variable-dialog.styl b/dashboard/src/app/workspaces/workspace-details/environments/list-env-variables/edit-variable-dialog/edit-variable-dialog.styl index 74c93924b7c..d202461e897 100644 --- a/dashboard/src/app/workspaces/workspace-details/environments/list-env-variables/edit-variable-dialog/edit-variable-dialog.styl +++ b/dashboard/src/app/workspaces/workspace-details/environments/list-env-variables/edit-variable-dialog/edit-variable-dialog.styl @@ -1,13 +1,29 @@ .edit-variable-dialog-content + div.che-label-container + border-bottom none + + div.che-label-container-label + min-width 100px + width 100px + + div.che-input-desktop-value-column + min-width 100% + + input, textarea + width inherit + + .che-label-container:last-child + padding-bottom 0 + + button + margin 0 0 0 10px + + .che-label-container + padding 15px 0 + .edit-variable-dialog-input margin -6px 0 .edit-variable-dialog-textarea margin -8px 0 - - button - margin 0 30px 0 0 - - input, textarea - width 300px diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.ts b/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.ts index 0fded5d2c1f..c15cef52323 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.ts +++ b/dashboard/src/app/workspaces/workspace-details/workspace-details.controller.ts @@ -590,9 +590,16 @@ export class WorkspaceDetailsController { } stopWorkspace(): void { - let promise = this.cheWorkspace.stopWorkspace(this.workspaceId, this.getAutoSnapshot()); + let createSnapshot: boolean; + if (this.getWorkspaceStatus() === 'STARTING') { + createSnapshot = false; + } else { + createSnapshot = this.getAutoSnapshot(); + } + let promise = this.cheWorkspace.stopWorkspace(this.workspaceId, createSnapshot); - promise.then(() => {}, (error: any) => { + promise.then(() => { + }, (error: any) => { this.cheNotification.showError(error.data.message !== null ? error.data.message : 'Stop workspace failed.'); this.$log.error(error); }); diff --git a/dashboard/src/app/workspaces/workspace-details/workspace-details.html b/dashboard/src/app/workspaces/workspace-details/workspace-details.html index 65559cfe676..c1f61ddb3cf 100644 --- a/dashboard/src/app/workspaces/workspace-details/workspace-details.html +++ b/dashboard/src/app/workspaces/workspace-details/workspace-details.html @@ -88,14 +88,16 @@
- - +
diff --git a/dashboard/src/components/api/builder/che-workspace-builder.ts b/dashboard/src/components/api/builder/che-workspace-builder.ts index 084290ac393..c1178ee99fd 100644 --- a/dashboard/src/components/api/builder/che-workspace-builder.ts +++ b/dashboard/src/components/api/builder/che-workspace-builder.ts @@ -10,6 +10,11 @@ */ 'use strict'; +export interface IWorkspaceAttributes { + created: number; + updated?: number; + [propName: string]: string | number; +} /** * This class is providing a builder for Workspace @@ -20,6 +25,7 @@ export class CheWorkspaceBuilder { constructor() { this.workspace = { + name: 'test', temporary: false, config: { projects: [] @@ -28,27 +34,32 @@ export class CheWorkspaceBuilder { } - withName(name: string) { + withName(name: string): CheWorkspaceBuilder { this.workspace.config.name = name; return this; } - withId(id: string) { + withId(id: string): CheWorkspaceBuilder { this.workspace.id = id; return this; } - withTemporary(temporary: boolean) { + withAttributes(attributes: IWorkspaceAttributes): CheWorkspaceBuilder { + this.workspace.attributes = attributes; + return this; + } + + withTemporary(temporary: boolean): CheWorkspaceBuilder { this.workspace.temporary = temporary; return this; } - withRuntime(runtime: any) { + withRuntime(runtime: any): CheWorkspaceBuilder { this.workspace.runtime = runtime; return this; } - build() { + build(): che.IWorkspace { return this.workspace; } diff --git a/dashboard/src/components/api/che-workspace.factory.ts b/dashboard/src/components/api/che-workspace.factory.ts index f74ace75d53..c3f37a7a465 100644 --- a/dashboard/src/components/api/che-workspace.factory.ts +++ b/dashboard/src/components/api/che-workspace.factory.ts @@ -447,7 +447,10 @@ export class CheWorkspace { */ stopWorkspace(workspaceId: string, createSnapshot: boolean): ng.IPromise { createSnapshot = createSnapshot === undefined ? this.getAutoSnapshotSettings() : createSnapshot; - return this.remoteWorkspaceAPI.stopWorkspace({workspaceId: workspaceId, createSnapshot: createSnapshot}, {}).$promise; + return this.remoteWorkspaceAPI.stopWorkspace({ + workspaceId: workspaceId, + createSnapshot: createSnapshot + }, {}).$promise; } /** @@ -458,7 +461,6 @@ export class CheWorkspace { */ updateWorkspace(workspaceId: string, data: che.IWorkspace): ng.IPromise { let defer = this.$q.defer(); - let promise = this.remoteWorkspaceAPI.updateWorkspace({workspaceId: workspaceId}, data).$promise; promise.then((data: che.IWorkspace) => { this.workspacesById.set(data.id, data); @@ -609,13 +611,17 @@ export class CheWorkspace { * * @returns {IPromise} */ - fetchWorkspaceSettings(): ng.IPromise { + fetchWorkspaceSettings(): ng.IPromise { let promise = this.remoteWorkspaceAPI.getSettings().$promise; - let resultPromise = promise.then((settings: any) => { + return promise.then((settings: any) => { this.workspaceSettings = settings; + return this.workspaceSettings; + }, (error: any) => { + if (error.status === 304) { + return this.workspaceSettings; + } + return this.$q.reject(error); }); - - return resultPromise; } /** diff --git a/dashboard/src/components/api/test/che-http-backend.ts b/dashboard/src/components/api/test/che-http-backend.ts index 37de914ef54..b2346b820a4 100644 --- a/dashboard/src/components/api/test/che-http-backend.ts +++ b/dashboard/src/components/api/test/che-http-backend.ts @@ -17,6 +17,9 @@ */ export class CheHttpBackend { + private isAutoSnapshot: boolean = false; + private isAutoRestore: boolean = false; + /** * Constructor to use */ @@ -52,6 +55,13 @@ export class CheHttpBackend { this.addWorkspaceAgent(key, tmpWorkspace.runtime); this.httpBackend.when('GET', '/api/workspace/' + key).respond(tmpWorkspace); } + + let workspacSettings = { + 'che.workspace.auto_snapshot': this.isAutoSnapshot, + 'che.workspace.auto_restore': this.isAutoRestore + }; + this.httpBackend.when('GET', '/api/workspace/settings').respond(200, workspacSettings); + this.httpBackend.when('OPTIONS', '/api/').respond({}); this.httpBackend.when('GET', '/api/workspace/settings').respond({}); @@ -90,6 +100,22 @@ export class CheHttpBackend { } + /** + * Set workspace auto snapshot status + * @param isAutoSnapshot {boolean} + */ + setWorkspaceAutoSnapshot(isAutoSnapshot: boolean) { + this.isAutoSnapshot = isAutoSnapshot; + } + + /** + * Set workspace auto restore status + * @param isAutoRestore {boolean} + */ + setWorkspaceAutoRestore(isAutoRestore: boolean) { + this.isAutoRestore = isAutoRestore; + } + /** * Add the given workspaces on this backend diff --git a/dashboard/src/components/service/confirm-dialog/che-confirm-dialog.html b/dashboard/src/components/service/confirm-dialog/che-confirm-dialog.html index 9b588bf4711..ebfeb24e716 100644 --- a/dashboard/src/components/service/confirm-dialog/che-confirm-dialog.html +++ b/dashboard/src/components/service/confirm-dialog/che-confirm-dialog.html @@ -1,12 +1,14 @@
{{cheConfirmDialogController.content}}
- - - - +
+ + + + +
diff --git a/dashboard/src/components/service/confirm-dialog/che-confirm-dialog.styl b/dashboard/src/components/service/confirm-dialog/che-confirm-dialog.styl index 6103a2e83e5..6ad3b9c3367 100644 --- a/dashboard/src/components/service/confirm-dialog/che-confirm-dialog.styl +++ b/dashboard/src/components/service/confirm-dialog/che-confirm-dialog.styl @@ -2,3 +2,9 @@ width 100% margin-top 10px min-height 50px + + div:last-child + margin 15px 0 0 0 + + button + margin 0 0 0 10px diff --git a/dashboard/src/components/widget/list/che-list-header.styl b/dashboard/src/components/widget/list/che-list-header.styl index e1ccecf190e..58e22a850c5 100644 --- a/dashboard/src/components/widget/list/che-list-header.styl +++ b/dashboard/src/components/widget/list/che-list-header.styl @@ -32,7 +32,7 @@ .che-list-add-button > *, .che-list-import-button > *, .che-list-delete-button > * - display inline-block + display inline box-sizing content-box min-width 34px min-height 34px diff --git a/dashboard/src/components/widget/list/che-list-item.styl b/dashboard/src/components/widget/list/che-list-item.styl index 9f33656e1f5..b6851d920d1 100644 --- a/dashboard/src/components/widget/list/che-list-item.styl +++ b/dashboard/src/components/widget/list/che-list-item.styl @@ -9,7 +9,7 @@ border-bottom 1px solid $list-separator-color .che-list-item-content - display inline-block + display inline line-height 40px overflow hidden width 100% diff --git a/dashboard/src/components/widget/text-info/che-text-info.directive.ts b/dashboard/src/components/widget/text-info/che-text-info.directive.ts index 8f7f3cc4543..9959c399d0a 100644 --- a/dashboard/src/components/widget/text-info/che-text-info.directive.ts +++ b/dashboard/src/components/widget/text-info/che-text-info.directive.ts @@ -31,7 +31,7 @@ export class CheTextInfo { textValue: '=cheText', hrefValue: '=cheHref', labelName: '@cheLabelName', - copyClipboard: '@cheCopyClipboard' + copyClipboard: '=cheCopyClipboard' }; }