From edcf42cf77b9ca38f56e168c15120f2834123ab6 Mon Sep 17 00:00:00 2001 From: ziajka Date: Thu, 17 May 2018 13:46:43 +0200 Subject: [PATCH 1/2] Don't delete items on topology when in readonly mode --- .../project-map-shortcuts.component.html | 0 .../project-map-shortcuts.component.spec.ts | 93 +++++++++++++++++-- .../project-map-shortcuts.component.ts | 17 ++-- .../project-map/project-map.component.html | 2 +- src/app/shared/models/project.ts | 1 + .../shared/services/settings.service.spec.ts | 4 +- .../shared/services/symbol.service.spec.ts | 3 - src/app/shared/services/toaster.service.ts | 20 ++++ yarn.lock | 42 ++------- 9 files changed, 129 insertions(+), 53 deletions(-) delete mode 100644 src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.html diff --git a/src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.html b/src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.html deleted file mode 100644 index e69de29b..00000000 diff --git a/src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.spec.ts b/src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.spec.ts index ca9f75ab..b3d0e43c 100644 --- a/src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.spec.ts +++ b/src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.spec.ts @@ -1,25 +1,102 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { inject } from "@angular/core/testing"; + +import { mock, instance, capture, when } from "ts-mockito"; +import { HotkeyModule, HotkeysService, Hotkey } from "angular2-hotkeys"; +import { Observable } from "rxjs"; import { ProjectMapShortcutsComponent } from './project-map-shortcuts.component'; +import { + ToasterService, + MockedToasterService +} from "../../shared/services/toaster.service"; +import { NodeService } from "../../shared/services/node.service"; +import { HttpServer } from "../../shared/services/http-server.service"; +import { SelectionManager } from "../../cartography/shared/managers/selection-manager"; +import { Server } from "../../shared/models/server"; +import { Node } from "../../cartography/shared/models/node"; +import { Project } from "../../shared/models/project"; + describe('ProjectMapShortcutsComponent', () => { let component: ProjectMapShortcutsComponent; let fixture: ComponentFixture; + let hotkeyServiceMock: HotkeysService; + let hotkeyServiceInstanceMock: HotkeysService; + let nodeServiceMock: NodeService; beforeEach(async(() => { + nodeServiceMock = mock(NodeService); + hotkeyServiceMock = mock(HotkeysService); + hotkeyServiceInstanceMock = instance(hotkeyServiceMock); TestBed.configureTestingModule({ + imports: [ + HotkeyModule.forRoot(), + HttpClientTestingModule + ], + providers: [ + HttpServer, + { provide: NodeService, useFactory: () => instance(nodeServiceMock)}, + { provide: HotkeysService, useFactory: () => hotkeyServiceInstanceMock}, + { provide: ToasterService, useClass: MockedToasterService}, + ], declarations: [ ProjectMapShortcutsComponent ] }) .compileComponents(); })); - // beforeEach(() => { - // fixture = TestBed.createComponent(ProjectMapShortcutsComponent); - // component = fixture.componentInstance; - // fixture.detectChanges(); - // }); + beforeEach(() => { + fixture = TestBed.createComponent(ProjectMapShortcutsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should bind delete key', () => { + component.ngOnInit(); + const [hotkey] = capture(hotkeyServiceMock.add).last(); + expect((hotkey as Hotkey).combo).toEqual([ 'del' ]); + expect((hotkey as Hotkey).callback).toEqual(component.onDeleteHandler); + }); + + it('should remove binding', () => { + component.ngOnDestroy(); + const [hotkey] = capture(hotkeyServiceMock.remove).last(); + expect((hotkey as Hotkey).combo).toEqual([ 'del' ]); + }); + + describe('onDeleteHandler', () => { + beforeEach(() => { + const selectionManagerMock = mock(SelectionManager); + const node = new Node(); + node.node_id = "nodeid"; + const server = new Server(); + const project = new Project(); + + when(selectionManagerMock.getSelectedNodes()).thenReturn([node]); + when(nodeServiceMock.delete(server, node)) + .thenReturn(Observable.of(node).mapTo(null)); + + component.project = project; + component.server = server; + component.selectionManager = instance(selectionManagerMock); + }); + + it('should handle delete', inject([ToasterService], (toaster: MockedToasterService) => { + component.project.readonly = false; + component.onDeleteHandler(null); + expect(toaster.successes).toEqual(["Node has been deleted"]); + })); + + it('should not delete when project in readonly mode', inject([ToasterService], (toaster: MockedToasterService) => { + component.project.readonly = true; + component.onDeleteHandler(null); + expect(toaster.successes).toEqual([]); + })); + }); - // it('should create', () => { - // expect(component).toBeTruthy(); - // }); }); diff --git a/src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.ts b/src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.ts index bbd5d77f..6b64476e 100644 --- a/src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.ts +++ b/src/app/project-map/project-map-shortcuts/project-map-shortcuts.component.ts @@ -5,13 +5,15 @@ import { SelectionManager } from '../../cartography/shared/managers/selection-ma import { NodeService } from '../../shared/services/node.service'; import { Server } from '../../shared/models/server'; import { ToasterService } from '../../shared/services/toaster.service'; +import { Project } from "../../shared/models/project"; @Component({ selector: 'app-project-map-shortcuts', - templateUrl: './project-map-shortcuts.component.html' + template: '' }) export class ProjectMapShortcutsComponent implements OnInit, OnDestroy { + @Input() project: Project; @Input() server: Server; @Input() selectionManager: SelectionManager; @@ -24,7 +26,12 @@ export class ProjectMapShortcutsComponent implements OnInit, OnDestroy { ) { } ngOnInit() { - this.deleteHotkey = new Hotkey('del', (event: KeyboardEvent): boolean => { + this.deleteHotkey = new Hotkey('del', this.onDeleteHandler); + this.hotkeysService.add(this.deleteHotkey); + } + + onDeleteHandler(event: KeyboardEvent):boolean { + if (!this.project.readonly) { const selectedNodes = this.selectionManager.getSelectedNodes(); if (selectedNodes) { selectedNodes.forEach((node) => { @@ -33,10 +40,8 @@ export class ProjectMapShortcutsComponent implements OnInit, OnDestroy { }); }); } - return false; - }); - - this.hotkeysService.add(this.deleteHotkey); + } + return false; } ngOnDestroy() { diff --git a/src/app/project-map/project-map.component.html b/src/app/project-map/project-map.component.html index 45900a09..15c4b6cb 100644 --- a/src/app/project-map/project-map.component.html +++ b/src/app/project-map/project-map.component.html @@ -55,4 +55,4 @@ - + diff --git a/src/app/shared/models/project.ts b/src/app/shared/models/project.ts index 3578f378..49301e55 100644 --- a/src/app/shared/models/project.ts +++ b/src/app/shared/models/project.ts @@ -9,4 +9,5 @@ export class Project { scene_height: number; scene_width: number; status: string; + readonly: boolean = true; } diff --git a/src/app/shared/services/settings.service.spec.ts b/src/app/shared/services/settings.service.spec.ts index d7b473a7..290cdaba 100644 --- a/src/app/shared/services/settings.service.spec.ts +++ b/src/app/shared/services/settings.service.spec.ts @@ -64,7 +64,9 @@ describe('SettingsService', () => { let changedSettings: Settings; service.set('crash_reports', true); - service.subscribe(settings => changedSettings = settings); + service.subscribe(settings => { + changedSettings = settings + }); service.set('crash_reports', false); expect(changedSettings.crash_reports).toEqual(false); diff --git a/src/app/shared/services/symbol.service.spec.ts b/src/app/shared/services/symbol.service.spec.ts index 38e36191..90a7ad3d 100644 --- a/src/app/shared/services/symbol.service.spec.ts +++ b/src/app/shared/services/symbol.service.spec.ts @@ -4,8 +4,6 @@ import { HttpClient } from '@angular/common/http'; import { HttpTestingController, HttpClientTestingModule } from '@angular/common/http/testing'; import { HttpServer } from './http-server.service'; import { Server } from '../models/server'; -import { Node } from '../../cartography/shared/models/node'; -import { Port } from '../models/port'; import { getTestServer } from './testing'; import { SymbolService } from './symbol.service'; import { Symbol } from '../../cartography/shared/models/symbol'; @@ -61,7 +59,6 @@ describe('SymbolService', () => { })); it('should load symbols', inject([SymbolService], (service: SymbolService) => { - let call = 0; service.load(server).subscribe(); const req = httpTestingController.expectOne( diff --git a/src/app/shared/services/toaster.service.ts b/src/app/shared/services/toaster.service.ts index eebf9dfd..472337a9 100644 --- a/src/app/shared/services/toaster.service.ts +++ b/src/app/shared/services/toaster.service.ts @@ -13,3 +13,23 @@ export class ToasterService { this.snackbar.open(message, null, { duration: 2000 }); } } + + +@Injectable() +export class MockedToasterService { + public errors: string[]; + public successes: string[]; + + constructor() { + this.errors = []; + this.successes = []; + } + + public error(message: string) { + this.errors.push(message); + } + + public success(message: string) { + this.successes.push(message); + } +} diff --git a/yarn.lock b/yarn.lock index 4a49358a..3099cca0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -594,14 +594,14 @@ ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" -ansi-regex@*, ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -2282,7 +2282,7 @@ debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, de dependencies: ms "2.0.0" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -4061,7 +4061,7 @@ import-local@^1.0.0: pkg-dir "^2.0.0" resolve-cwd "^2.0.0" -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -4892,10 +4892,6 @@ lockfile@~1.0.2: dependencies: signal-exit "^3.0.2" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -4903,28 +4899,10 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*: - version "3.0.1" - resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" - -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" -lodash._getnative@*, lodash._getnative@^3.0.0: - version "3.9.1" - resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" - lodash._root@~3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692" @@ -4941,10 +4919,6 @@ lodash.mergewith@^4.6.0: version "4.6.1" resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927" -lodash.restparam@*: - version "3.6.1" - resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" - lodash.tail@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/lodash.tail/-/lodash.tail-4.1.1.tgz#d2333a36d9e7717c8ad2f7cacafec7c32b444664" @@ -6757,7 +6731,7 @@ readable-stream@~2.1.5: string_decoder "~0.10.x" util-deprecate "~1.0.1" -readdir-scoped-modules@*, readdir-scoped-modules@^1.0.0: +readdir-scoped-modules@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747" dependencies: @@ -8384,7 +8358,7 @@ uws@~9.14.0: version "9.14.0" resolved "https://registry.yarnpkg.com/uws/-/uws-9.14.0.tgz#fac8386befc33a7a3705cbd58dc47b430ca4dd95" -validate-npm-package-license@*, validate-npm-package-license@^3.0.1: +validate-npm-package-license@^3.0.1: version "3.0.3" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" dependencies: From bd54786f20337bb95322f17e251058d57a0dbf1e Mon Sep 17 00:00:00 2001 From: ziajka Date: Thu, 17 May 2018 16:54:09 +0200 Subject: [PATCH 2/2] Hide layer up/down when project is readonly --- src/app/project-map/project-map.component.html | 8 ++++---- .../node-context-menu.component.html | 4 ++-- .../node-context-menu/node-context-menu.component.ts | 12 +++++++----- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/app/project-map/project-map.component.html b/src/app/project-map/project-map.component.html index 15c4b6cb..8a437510 100644 --- a/src/app/project-map/project-map.component.html +++ b/src/app/project-map/project-map.component.html @@ -21,7 +21,7 @@ - + @@ -33,20 +33,20 @@ - + - + - + diff --git a/src/app/shared/node-context-menu/node-context-menu.component.html b/src/app/shared/node-context-menu/node-context-menu.component.html index 986aceaf..bd0a086b 100644 --- a/src/app/shared/node-context-menu/node-context-menu.component.html +++ b/src/app/shared/node-context-menu/node-context-menu.component.html @@ -3,7 +3,7 @@ - - + + diff --git a/src/app/shared/node-context-menu/node-context-menu.component.ts b/src/app/shared/node-context-menu/node-context-menu.component.ts index f37b1a8f..1fda55c5 100644 --- a/src/app/shared/node-context-menu/node-context-menu.component.ts +++ b/src/app/shared/node-context-menu/node-context-menu.component.ts @@ -1,8 +1,9 @@ -import {ChangeDetectorRef, Component, Input, OnInit, ViewChild} from '@angular/core'; -import {MatMenuTrigger} from "@angular/material"; -import {DomSanitizer} from "@angular/platform-browser"; -import {Node} from "../../cartography/shared/models/node"; -import {Server} from "../models/server"; +import { ChangeDetectorRef, Component, Input, OnInit, ViewChild } from '@angular/core'; +import { MatMenuTrigger } from "@angular/material"; +import { DomSanitizer } from "@angular/platform-browser"; +import { Node } from "../../cartography/shared/models/node"; +import { Server } from "../models/server"; +import { Project } from "../models/project"; @Component({ @@ -11,6 +12,7 @@ import {Server} from "../models/server"; styleUrls: ['./node-context-menu.component.scss'] }) export class NodeContextMenuComponent implements OnInit { + @Input() project: Project; @Input() server: Server; @ViewChild(MatMenuTrigger) contextMenu: MatMenuTrigger;