mirror of
https://github.com/GNS3/gns3-web-ui.git
synced 2024-12-18 20:47:51 +00:00
Merge with master
This commit is contained in:
commit
9478f8f6f9
@ -24,7 +24,7 @@ build_script:
|
||||
- "%PYTHON%\\python.exe scripts\\build.py build_exe -b dist/exe.gns3server -s"
|
||||
- "%PYTHON%\\python.exe scripts\\build.py validate -b dist"
|
||||
- "%PYTHON%\\python.exe scripts\\build.py download_dependencies -b dist"
|
||||
- yarn electron-builder --win --x64
|
||||
- yarn electron-builder --win --x64 --publish always
|
||||
|
||||
test: off
|
||||
|
||||
|
@ -54,7 +54,7 @@ jobs:
|
||||
- run:
|
||||
name: Dist project
|
||||
command: |
|
||||
yarn electron-builder --mac --x64
|
||||
yarn electron-builder --mac --x64 --publish always
|
||||
|
||||
- run:
|
||||
name: Gather artifacts
|
||||
|
4
.sentryclirc
Normal file
4
.sentryclirc
Normal file
@ -0,0 +1,4 @@
|
||||
[defaults]
|
||||
url = https://sentry.io/
|
||||
org = gns3
|
||||
project = gns3-web-ui
|
13
.travis.yml
13
.travis.yml
@ -68,4 +68,15 @@ after_script:
|
||||
python3 scripts/build.py download
|
||||
python3 scripts/build.py build_exe -b dist/exe.gns3server -s
|
||||
python3 scripts/build.py validate -b dist
|
||||
- yarn electron-builder --linux --x64
|
||||
- yarn electron-builder --linux --x64 --publish always
|
||||
|
||||
# build sourcemaps and upload to Sentry
|
||||
# fix node issue with memory
|
||||
- |
|
||||
if [ -n "$TRAVIS_TAG" ];
|
||||
export NODE_OPTIONS=--max_old_space_size=4096
|
||||
export RELEASE_VERSION=$(node -e "const fs = require('fs'); let p = fs.readFileSync('package.json'); console.log(JSON.parse(p).version);")
|
||||
yarn ng build --configuration=production --base-href /static/web-ui/
|
||||
yarn sentry-cli releases new $RELEASE_VERSION
|
||||
yarn sentry-cli releases files $RELEASE_VERSION upload-sourcemaps dist/
|
||||
fi
|
||||
|
@ -32,7 +32,11 @@
|
||||
"production": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"sourceMap": {
|
||||
"hidden": true,
|
||||
"scripts": true,
|
||||
"styles": false
|
||||
},
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"aot": true,
|
||||
|
@ -4,6 +4,7 @@ productName: "GNS3 Web UI"
|
||||
artifactName: "${productName}-${os}-${arch}-${version}.${ext}"
|
||||
asar: true
|
||||
compression: normal
|
||||
generateUpdatesFilesForAllChannels: true
|
||||
|
||||
directories:
|
||||
output: build
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "gns3-web-ui",
|
||||
"version": "0.0.1-beta.0",
|
||||
"version": "2019.1.0-alpha.4dev",
|
||||
"author": {
|
||||
"name": "GNS3 Technology Inc.",
|
||||
"email": "developers@gns3.com"
|
||||
@ -77,6 +77,7 @@
|
||||
"@angular/cli": "^7.3.3",
|
||||
"@angular/compiler-cli": "^7.2.7",
|
||||
"@angular/language-service": "^7.2.7",
|
||||
"@sentry/cli": "^1.40.0",
|
||||
"@sentry/electron": "^0.16.0",
|
||||
"@types/jasmine": "~3.3.9",
|
||||
"@types/jasminewd2": "~2.0.6",
|
||||
|
@ -211,6 +211,7 @@ def build_command(arguments):
|
||||
("gns3server/appliances", "appliances"),
|
||||
("gns3server/templates", "templates"),
|
||||
("gns3server/symbols", "symbols"),
|
||||
("gns3server/static/web-ui", "static/web-ui")
|
||||
]
|
||||
|
||||
include_files = [(os.path.join(source_directory, x), y) for x, y in include_files]
|
||||
|
@ -6,7 +6,7 @@ import { ServersComponent } from './components/servers/servers.component';
|
||||
import { ProjectsComponent } from './components/projects/projects.component';
|
||||
import { DefaultLayoutComponent } from './layouts/default-layout/default-layout.component';
|
||||
import { SettingsComponent } from './components/settings/settings.component';
|
||||
import { LocalServerComponent } from './components/local-server/local-server.component';
|
||||
import { BundledServerFinderComponent } from './components/bundled-server-finder/bundled-server-finder.component';
|
||||
import { PreferencesComponent } from './components/preferences/preferences.component';
|
||||
import { QemuPreferencesComponent } from './components/preferences/qemu/qemu-preferences/qemu-preferences.component';
|
||||
import { QemuVmTemplatesComponent } from './components/preferences/qemu/qemu-vm-templates/qemu-vm-templates.component';
|
||||
@ -60,7 +60,7 @@ const routes: Routes = [
|
||||
children: [
|
||||
{ path: '', redirectTo: 'servers', pathMatch: 'full' },
|
||||
{ path: 'servers', component: ServersComponent },
|
||||
{ path: 'local', component: LocalServerComponent },
|
||||
{ path: 'bundled', component: BundledServerFinderComponent },
|
||||
{ path: 'server/:server_id/projects', component: ProjectsComponent },
|
||||
{ path: 'settings', component: SettingsComponent },
|
||||
{ path: 'settings/console', component: ConsoleComponent },
|
||||
@ -122,6 +122,10 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: 'server/:server_id/project/:project_id', component: ProjectMapComponent,
|
||||
},
|
||||
{
|
||||
path: '**',
|
||||
redirectTo: 'servers'
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -58,7 +58,7 @@ import { ProjectMapShortcutsComponent } from './components/project-map/project-m
|
||||
import { SettingsComponent } from './components/settings/settings.component';
|
||||
import { SettingsService } from './services/settings.service';
|
||||
|
||||
import { LocalServerComponent } from './components/local-server/local-server.component';
|
||||
import { BundledServerFinderComponent } from './components/bundled-server-finder/bundled-server-finder.component';
|
||||
import { ProgressComponent } from './common/progress/progress.component';
|
||||
import { ProgressService } from './common/progress/progress.service';
|
||||
import { version } from './version';
|
||||
@ -168,8 +168,18 @@ import { ListOfSnapshotsComponent } from './components/snapshots/list-of-snapsho
|
||||
import { DateFilter } from './filters/dateFilter.pipe';
|
||||
import { NameFilter } from './filters/nameFilter.pipe';
|
||||
import { CustomAdaptersComponent } from './components/preferences/common/custom-adapters/custom-adapters.component';
|
||||
|
||||
import { ConsoleDeviceActionComponent } from './components/project-map/context-menu/actions/console-device-action/console-device-action.component';
|
||||
import { ConsoleComponent } from './components/settings/console/console.component';
|
||||
import { NodesMenuComponent } from './components/project-map/nodes-menu/nodes-menu.component';
|
||||
import { PacketFiltersActionComponent } from './components/project-map/context-menu/actions/packet-filters-action/packet-filters-action.component';
|
||||
import { PacketFiltersDialogComponent } from './components/project-map/packet-capturing/packet-filters/packet-filters.component';
|
||||
import { HelpDialogComponent } from './components/project-map/help-dialog/help-dialog.component';
|
||||
import { StartCaptureActionComponent } from './components/project-map/context-menu/actions/start-capture/start-capture-action.component';
|
||||
import { StartCaptureDialogComponent } from './components/project-map/packet-capturing/start-capture/start-capture.component';
|
||||
import { SuspendLinkActionComponent } from './components/project-map/context-menu/actions/suspend-link/suspend-link-action.component';
|
||||
import { ResumeLinkActionComponent } from './components/project-map/context-menu/actions/resume-link-action/resume-link-action.component';
|
||||
import { StopCaptureActionComponent } from './components/project-map/context-menu/actions/stop-capture/stop-capture-action.component';
|
||||
|
||||
if (environment.production) {
|
||||
Raven.config('https://b2b1cfd9b043491eb6b566fd8acee358@sentry.io/842726', {
|
||||
@ -204,10 +214,15 @@ if (environment.production) {
|
||||
EditStyleActionComponent,
|
||||
EditTextActionComponent,
|
||||
DeleteActionComponent,
|
||||
PacketFiltersActionComponent,
|
||||
StartCaptureActionComponent,
|
||||
StopCaptureActionComponent,
|
||||
ResumeLinkActionComponent,
|
||||
SuspendLinkActionComponent,
|
||||
ProjectMapShortcutsComponent,
|
||||
SettingsComponent,
|
||||
PreferencesComponent,
|
||||
LocalServerComponent,
|
||||
BundledServerFinderComponent,
|
||||
ProgressComponent,
|
||||
ServerDiscoveryComponent,
|
||||
NodeSelectInterfaceComponent,
|
||||
@ -225,6 +240,7 @@ if (environment.production) {
|
||||
InstallSoftwareComponent,
|
||||
StyleEditorDialogComponent,
|
||||
TextEditorDialogComponent,
|
||||
PacketFiltersDialogComponent,
|
||||
QemuPreferencesComponent,
|
||||
QemuVmTemplatesComponent,
|
||||
AddQemuVmTemplateComponent,
|
||||
@ -258,6 +274,8 @@ if (environment.production) {
|
||||
VmwareTemplateDetailsComponent,
|
||||
AddVmwareTemplateComponent,
|
||||
DeleteConfirmationDialogComponent,
|
||||
HelpDialogComponent,
|
||||
StartCaptureDialogComponent,
|
||||
DeleteTemplateComponent,
|
||||
DockerTemplatesComponent,
|
||||
AddDockerTemplateComponent,
|
||||
@ -277,7 +295,8 @@ if (environment.production) {
|
||||
ListOfSnapshotsComponent,
|
||||
CustomAdaptersComponent,
|
||||
ConsoleDeviceActionComponent,
|
||||
ConsoleComponent
|
||||
ConsoleComponent,
|
||||
NodesMenuComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
@ -357,9 +376,12 @@ if (environment.production) {
|
||||
ImportProjectDialogComponent,
|
||||
ConfirmationDialogComponent,
|
||||
StyleEditorDialogComponent,
|
||||
PacketFiltersDialogComponent,
|
||||
TextEditorDialogComponent,
|
||||
SymbolsComponent,
|
||||
DeleteConfirmationDialogComponent
|
||||
DeleteConfirmationDialogComponent,
|
||||
HelpDialogComponent,
|
||||
StartCaptureDialogComponent
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
@ -15,9 +15,11 @@ export class LinkToMapLinkConverter implements Converter<Link, MapLink> {
|
||||
mapLink.captureFileName = link.capture_file_name;
|
||||
mapLink.captureFilePath = link.capture_file_path;
|
||||
mapLink.capturing = link.capturing;
|
||||
mapLink.filters = link.filters;
|
||||
mapLink.linkType = link.link_type;
|
||||
mapLink.nodes = link.nodes.map(linkNode => this.linkNodeToMapLinkNode.convert(linkNode, { link_id: link.link_id }));
|
||||
mapLink.projectId = link.project_id;
|
||||
mapLink.suspend = link.suspend;
|
||||
return mapLink;
|
||||
}
|
||||
}
|
||||
|
@ -15,9 +15,11 @@ export class MapLinkToLinkConverter implements Converter<MapLink, Link> {
|
||||
link.capture_file_name = mapLink.captureFileName;
|
||||
link.capture_file_path = mapLink.captureFilePath;
|
||||
link.capturing = mapLink.capturing;
|
||||
link.filters = mapLink.filters;
|
||||
link.link_type = mapLink.linkType;
|
||||
link.nodes = mapLink.nodes.map(mapLinkNode => this.mapLinkNodeToMapLinkNode.convert(mapLinkNode));
|
||||
link.project_id = mapLink.projectId;
|
||||
link.suspend = mapLink.suspend;
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { TextElement } from '../models/drawings/text-element';
|
||||
import { MapDrawing } from '../models/map/map-drawing';
|
||||
import { MapLink } from '../models/map/map-link';
|
||||
|
||||
export class DataEventSource<T> {
|
||||
constructor(public datum: T, public dx: number, public dy: number) {}
|
||||
@ -30,3 +31,7 @@ export class TextEditedDataEvent {
|
||||
export class DrawingContextMenu {
|
||||
constructor(public event: any, public drawing: MapDrawing) {}
|
||||
}
|
||||
|
||||
export class LinkContextMenu {
|
||||
constructor(public event:any, public link: MapLink) {}
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
import { MapLinkNode } from './map-link-node';
|
||||
import { MapNode } from './map-node';
|
||||
import { Indexed } from '../../datasources/map-datasource';
|
||||
import { Filter } from '../../../models/filter';
|
||||
|
||||
export class MapLink implements Indexed {
|
||||
id: string;
|
||||
captureFileName: string;
|
||||
captureFilePath: string;
|
||||
capturing: boolean;
|
||||
filters?: Filter;
|
||||
linkType: string;
|
||||
nodes: MapLinkNode[];
|
||||
projectId: string;
|
||||
suspend: boolean;
|
||||
|
||||
distance: number; // this is not from server
|
||||
length: number; // this is not from server
|
||||
|
@ -57,6 +57,12 @@ export class MovingTool {
|
||||
});
|
||||
};
|
||||
|
||||
// disable zooming on wheel
|
||||
this.zoom.filter(() => {
|
||||
const e: D3ZoomEvent<SVGSVGElement, any> = event;
|
||||
return e.type === 'mousedown';
|
||||
});
|
||||
|
||||
this.zoom.on('zoom', onZoom);
|
||||
selection.call(this.zoom);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable, EventEmitter } from '@angular/core';
|
||||
|
||||
import { Widget } from './widget';
|
||||
import { SVGSelection } from '../models/types';
|
||||
@ -9,9 +9,13 @@ import { InterfaceLabelWidget } from './interface-label';
|
||||
import { InterfaceStatusWidget } from './interface-status';
|
||||
import { MapLink } from '../models/map/map-link';
|
||||
import { SelectionManager } from '../managers/selection-manager';
|
||||
import { event } from 'd3-selection';
|
||||
import { LinkContextMenu } from '../events/event-source';
|
||||
|
||||
@Injectable()
|
||||
export class LinkWidget implements Widget {
|
||||
public onContextMenu = new EventEmitter<LinkContextMenu>();
|
||||
|
||||
constructor(
|
||||
private multiLinkCalculatorHelper: MultiLinkCalculatorHelper,
|
||||
private interfaceLabelWidget: InterfaceLabelWidget,
|
||||
@ -32,6 +36,58 @@ export class LinkWidget implements Widget {
|
||||
return `translate (${translation.dx}, ${translation.dy})`;
|
||||
});
|
||||
|
||||
link_body.select('.capture-icon').remove();
|
||||
link_body
|
||||
.filter(l => { return l.capturing && !(l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)})
|
||||
.append<SVGGElement>('g')
|
||||
.on('contextmenu', (datum: MapLink) => {
|
||||
const evt = event;
|
||||
this.onContextMenu.emit(new LinkContextMenu(evt, datum));
|
||||
})
|
||||
.attr('class', 'capture-icon')
|
||||
.attr('transform', link => {
|
||||
return `translate (${(link.source.x + link.target.x)/2 + 24}, ${(link.source.y + link.target.y)/2 + 24}) scale(0.5)`
|
||||
})
|
||||
.attr('viewBox', '0 0 20 20')
|
||||
.append<SVGImageElement>('image')
|
||||
.attr("xlink:href", "assets/resources/images/inspect.svg");
|
||||
|
||||
link_body.select('.filter-capture-icon').remove();
|
||||
link_body
|
||||
.filter(l => { return l.capturing && (l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)})
|
||||
.append<SVGGElement>('g')
|
||||
.on('contextmenu', (datum: MapLink) => {
|
||||
const evt = event;
|
||||
this.onContextMenu.emit(new LinkContextMenu(evt, datum));
|
||||
})
|
||||
.attr('class', 'filter-capture-icon')
|
||||
.attr('transform', link => {
|
||||
return `translate (${(link.source.x + link.target.x)/2 + 24}, ${(link.source.y + link.target.y)/2 + 24}) scale(0.5)`
|
||||
})
|
||||
.attr('viewBox', '0 0 20 20')
|
||||
.append<SVGImageElement>('image')
|
||||
.attr("xlink:href", "assets/resources/images/filter-capture.svg");
|
||||
|
||||
link_body.select('.filter-icon').remove();
|
||||
link_body
|
||||
.filter(l => { return !l.capturing && (l.filters.bpf || l.filters.corrupt || l.filters.delay || l.filters.frequency_drop || l.filters.packet_loss)})
|
||||
.append<SVGGElement>('g')
|
||||
.on('contextmenu', (datum: MapLink) => {
|
||||
const evt = event;
|
||||
this.onContextMenu.emit(new LinkContextMenu(evt, datum));
|
||||
})
|
||||
.attr('class', 'filter-icon')
|
||||
.attr('width', '48px')
|
||||
.attr('height', '48px')
|
||||
.attr('transform', link => {
|
||||
return `translate (${(link.source.x + link.target.x)/2 + 24}, ${(link.source.y + link.target.y)/2 + 24}) scale(0.5)`
|
||||
})
|
||||
.attr('viewBox', '0 0 20 20')
|
||||
.append<SVGImageElement>('image')
|
||||
.attr('width', '48px')
|
||||
.attr('height', '48px')
|
||||
.attr("xlink:href", "assets/resources/images/filter.svg");
|
||||
|
||||
const serial_link_widget = new SerialLinkWidget();
|
||||
serial_link_widget.draw(link_body_merge);
|
||||
|
||||
|
@ -5,6 +5,14 @@ import { ToasterErrorHandler } from './toaster-error-handler';
|
||||
import { RavenErrorHandler } from './raven-error-handler';
|
||||
import { SettingsService } from '../../services/settings.service';
|
||||
import { MockedSettingsService } from '../../services/settings.service.spec';
|
||||
import { Injector } from '@angular/core';
|
||||
|
||||
class MockedToasterErrorHandler extends ToasterErrorHandler {
|
||||
handleError(err: any): void {
|
||||
const toasterService = this.injector.get(ToasterService);
|
||||
toasterService.error(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
describe('ToasterErrorHandler', () => {
|
||||
let handler: ToasterErrorHandler;
|
||||
@ -20,12 +28,13 @@ describe('ToasterErrorHandler', () => {
|
||||
]
|
||||
});
|
||||
|
||||
handler = TestBed.get(ToasterErrorHandler);
|
||||
handler = new MockedToasterErrorHandler(TestBed.get(Injector));
|
||||
toasterService = TestBed.get(ToasterService);
|
||||
});
|
||||
|
||||
it('should call toaster with error message', () => {
|
||||
handler.handleError(new Error('message'));
|
||||
|
||||
expect(toasterService.errors).toEqual(['message']);
|
||||
});
|
||||
});
|
||||
|
@ -0,0 +1 @@
|
||||
<app-progress></app-progress>
|
@ -1,16 +1,21 @@
|
||||
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { LocalServerComponent } from './local-server.component';
|
||||
import { BundledServerFinderComponent } from './bundled-server-finder.component';
|
||||
import { ServerService } from '../../services/server.service';
|
||||
import { MockedServerService } from '../../services/server.service.spec';
|
||||
import { Server } from '../../models/server';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ProgressService } from '../../common/progress/progress.service';
|
||||
import { MockedProgressService } from '../project-map/project-map.component.spec';
|
||||
|
||||
describe('LocalServerComponent', () => {
|
||||
let component: LocalServerComponent;
|
||||
let fixture: ComponentFixture<LocalServerComponent>;
|
||||
|
||||
describe('BundledServerFinderComponent', () => {
|
||||
let component: BundledServerFinderComponent;
|
||||
let fixture: ComponentFixture<BundledServerFinderComponent>;
|
||||
let router: any;
|
||||
let serverService: any;
|
||||
let progressService: MockedProgressService = new MockedProgressService();
|
||||
|
||||
beforeEach(async(() => {
|
||||
router = {
|
||||
@ -24,11 +29,16 @@ describe('LocalServerComponent', () => {
|
||||
spyOn(serverService, 'getLocalServer').and.returnValue(Promise.resolve(server));
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [{ provide: Router, useValue: router }, { provide: ServerService, useValue: serverService }],
|
||||
declarations: [LocalServerComponent]
|
||||
providers: [
|
||||
{ provide: Router, useValue: router },
|
||||
{ provide: ServerService, useValue: serverService },
|
||||
{ provide: ProgressService, useValue: progressService }
|
||||
],
|
||||
declarations: [BundledServerFinderComponent],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(LocalServerComponent);
|
||||
fixture = TestBed.createComponent(BundledServerFinderComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
}));
|
@ -0,0 +1,35 @@
|
||||
import { Component, OnInit, Inject } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ServerService } from '../../services/server.service';
|
||||
import { Server } from '../../models/server';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { ProgressService } from '../../common/progress/progress.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-bundled-server-finder',
|
||||
templateUrl: './bundled-server-finder.component.html',
|
||||
styleUrls: ['./bundled-server-finder.component.scss']
|
||||
})
|
||||
export class BundledServerFinderComponent implements OnInit {
|
||||
constructor(
|
||||
private router: Router,
|
||||
private serverService: ServerService,
|
||||
private progressService: ProgressService,
|
||||
@Inject(DOCUMENT) private document) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.progressService.activate();
|
||||
setTimeout(() =>
|
||||
{
|
||||
this.serverService.getLocalServer(
|
||||
this.document.location.hostname,
|
||||
parseInt(this.document.location.port, 10))
|
||||
.then((server: Server) => {
|
||||
this.progressService.deactivate();
|
||||
this.router.navigate(['/server', server.id, 'projects']);
|
||||
});
|
||||
},
|
||||
100);
|
||||
}
|
||||
}
|
@ -46,6 +46,10 @@ describe('DrawingAddedComponent', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.ngOnDestroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
@ -30,6 +30,11 @@ describe('DrawingDraggedComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DrawingDraggedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.ngOnDestroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
@ -37,7 +42,6 @@ describe('DrawingDraggedComponent', () => {
|
||||
});
|
||||
|
||||
it('should call drawing service when drawing is dragged', () => {
|
||||
fixture.detectChanges();
|
||||
const mapDrawingElement: DrawingElement = {
|
||||
width: 100,
|
||||
height: 100
|
||||
|
@ -33,6 +33,11 @@ describe('DrawingResizedComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DrawingResizedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.ngOnDestroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
@ -40,7 +45,6 @@ describe('DrawingResizedComponent', () => {
|
||||
});
|
||||
|
||||
it('should call drawing service when drawing is resized', () => {
|
||||
fixture.detectChanges();
|
||||
const mapDrawingElement: DrawingElement = {
|
||||
width: 100,
|
||||
height: 100
|
||||
|
@ -32,6 +32,11 @@ describe('InterfaceLabelDraggedComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(InterfaceLabelDraggedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.ngOnDestroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
@ -39,7 +44,6 @@ describe('InterfaceLabelDraggedComponent', () => {
|
||||
});
|
||||
|
||||
it('should call link service when interface label dragged', () => {
|
||||
fixture.detectChanges();
|
||||
const mapLinkNode: MapLinkNode = {
|
||||
id: 'sampleId',
|
||||
nodeId: 'sampleNodeId',
|
||||
|
@ -47,17 +47,21 @@ describe('LinkCreatedComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(LinkCreatedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
|
||||
project.project_id = 'sampleId';
|
||||
component.project = project;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.ngOnDestroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should call link service when link created', () => {
|
||||
fixture.detectChanges();
|
||||
const mapNode: MapNode = {
|
||||
id: 'sampleId',
|
||||
commandLine: 'sampleCommandLine',
|
||||
|
@ -30,6 +30,11 @@ describe('NodeDraggedComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NodeDraggedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.ngOnDestroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
@ -37,7 +42,6 @@ describe('NodeDraggedComponent', () => {
|
||||
});
|
||||
|
||||
it('should call node service when node dragged', () => {
|
||||
fixture.detectChanges();
|
||||
const mapNode: MapNode = {
|
||||
id: 'sampleId',
|
||||
commandLine: 'sampleCommandLine',
|
||||
|
@ -39,6 +39,11 @@ describe('NodeLabelDraggedComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NodeLabelDraggedComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.ngOnDestroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
@ -46,7 +51,6 @@ describe('NodeLabelDraggedComponent', () => {
|
||||
});
|
||||
|
||||
it('should call node service when node label dragged', () => {
|
||||
fixture.detectChanges();
|
||||
const mapLabel: MapLabel = {
|
||||
id: 'sample id',
|
||||
rotation: 0,
|
||||
|
@ -48,6 +48,10 @@ describe('TextAddedComponent', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.ngOnDestroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
@ -34,6 +34,10 @@ describe('TextEditedComponent', () => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.ngOnDestroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
@ -1,20 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { ServerService } from '../../services/server.service';
|
||||
import { Server } from '../../models/server';
|
||||
|
||||
@Component({
|
||||
selector: 'app-local-server',
|
||||
templateUrl: './local-server.component.html',
|
||||
styleUrls: ['./local-server.component.scss']
|
||||
})
|
||||
export class LocalServerComponent implements OnInit {
|
||||
constructor(private router: Router, private serverService: ServerService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.serverService.getLocalServer(location.hostname, parseInt(location.port, 10)).then((server: Server) => {
|
||||
this.router.navigate(['/server', server.id, 'projects']);
|
||||
});
|
||||
}
|
||||
}
|
@ -35,20 +35,28 @@ describe('CloudNodesAddTemplateComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
CloudNodesAddTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
imports: [
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
CommonModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes([{path: 'server/1/preferences/builtin/cloud-nodes', component: CloudNodesAddTemplateComponent}])
|
||||
],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService },
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
CloudNodesAddTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
|
@ -35,20 +35,30 @@ describe('EthernetHubsAddTemplateComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
EthernetHubsAddTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
imports: [
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
CommonModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes([{path: 'server/1/preferences/builtin/ethernet-hubs', component: EthernetHubsAddTemplateComponent}])
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
EthernetHubsAddTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
|
@ -35,20 +35,30 @@ describe('EthernetSwitchesAddTemplateComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
EthernetSwitchesAddTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
imports: [
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
CommonModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes([{path: 'server/1/preferences/builtin/ethernet-switches', component: EthernetSwitchesAddTemplateComponent}])
|
||||
],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: BuiltInTemplatesService, useValue: mockedBuiltInTemplatesService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
EthernetSwitchesAddTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput
|
||||
class="filename"
|
||||
type="text"
|
||||
[(ngModel)]="dockerTemplate.image"
|
||||
formControlName="filename"
|
||||
@ -45,6 +46,7 @@
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput
|
||||
class="templatename"
|
||||
type="text"
|
||||
[(ngModel)]="dockerTemplate.name"
|
||||
formControlName="templateName"
|
||||
@ -57,6 +59,7 @@
|
||||
<mat-form-field class="form-field">
|
||||
<input
|
||||
matInput
|
||||
class="networkadapter"
|
||||
type="number"
|
||||
[(ngModel)]="dockerTemplate.adapters"
|
||||
formControlName="adapters"
|
||||
@ -92,7 +95,7 @@
|
||||
</div>
|
||||
<div class="buttons-bar">
|
||||
<button mat-button class="cancel-button" (click)="goBack()">Cancel</button>
|
||||
<button mat-raised-button color="primary" (click)="addTemplate()">Add template</button>
|
||||
<button mat-raised-button class="add-button" color="primary" (click)="addTemplate()">Add template</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ComponentFixture, async, TestBed } from '@angular/core/testing';
|
||||
import { MatInputModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatFormFieldModule, MatAutocompleteModule, MatTableModule, MatStepperModule } from '@angular/material';
|
||||
import { MatInputModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatSelectModule, MatFormFieldModule, MatAutocompleteModule, MatTableModule, MatStepperModule, MatRadioModule, MatCommonModule } from '@angular/material';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ActivatedRoute, Route } from '@angular/router';
|
||||
import { of } from 'rxjs';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { MockedServerService } from '../../../../services/server.service.spec';
|
||||
@ -13,11 +13,13 @@ import { ToasterService } from '../../../../services/toaster.service';
|
||||
import { TemplateMocksService } from '../../../../services/template-mocks.service';
|
||||
import { MockedToasterService } from '../../../../services/toaster.service.spec';
|
||||
import { MockedActivatedRoute } from '../../preferences.component.spec';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule, AbstractControlDirective, FormControl } from '@angular/forms';
|
||||
import { DockerTemplate } from '../../../../models/templates/docker-template';
|
||||
import { AddDockerTemplateComponent } from './add-docker-template.component';
|
||||
import { DockerService } from '../../../../services/docker.service';
|
||||
import { DockerConfigurationService } from '../../../../services/docker-configuration.service';
|
||||
import { StepperOrientation, STEPPER_GLOBAL_OPTIONS, STEP_STATE, CdkStep } from '@angular/cdk/stepper';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
export class MockedDockerService {
|
||||
public addTemplate(server: Server, dockerTemplate: DockerTemplate) {
|
||||
@ -25,7 +27,8 @@ export class MockedDockerService {
|
||||
}
|
||||
}
|
||||
|
||||
describe('AddDockerTemplateComponent', () => {
|
||||
//Tests disabled due to instability
|
||||
xdescribe('AddDockerTemplateComponent', () => {
|
||||
let component: AddDockerTemplateComponent;
|
||||
let fixture: ComponentFixture<AddDockerTemplateComponent>;
|
||||
|
||||
@ -36,76 +39,165 @@ describe('AddDockerTemplateComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatStepperModule, FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: DockerService, useValue: mockedDockerService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService },
|
||||
{ provide: DockerConfigurationService, useClass: DockerConfigurationService }
|
||||
],
|
||||
declarations: [
|
||||
AddDockerTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
imports: [
|
||||
MatStepperModule,
|
||||
MatAutocompleteModule,
|
||||
MatCommonModule,
|
||||
MatRadioModule,
|
||||
FormsModule,
|
||||
MatTableModule,
|
||||
MatAutocompleteModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
MatSelectModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
CommonModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes([{path: 'server/1/preferences/docker/templates', component: AddDockerTemplateComponent}])
|
||||
],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: DockerService, useValue: mockedDockerService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService },
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService },
|
||||
{ provide: DockerConfigurationService, useClass: DockerConfigurationService },
|
||||
{ provide: AbstractControlDirective, useExisting: FormControl, useMulti: true },
|
||||
],
|
||||
declarations: [
|
||||
AddDockerTemplateComponent
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AddDockerTemplateComponent);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should open first step at start', async(() => {
|
||||
fixture.detectChanges();
|
||||
});
|
||||
fixture.whenStable().then(() => {
|
||||
let stepperComponent = fixture.debugElement
|
||||
.query(By.css('mat-vertical-stepper')).componentInstance;
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
expect(stepperComponent.selectedIndex).toBe(0);
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call add template', () => {
|
||||
spyOn(mockedDockerService, 'addTemplate').and.returnValue(of({} as DockerTemplate));
|
||||
component.virtualMachineForm.controls['filename'].setValue('sample name');
|
||||
component.containerNameForm.controls['templateName'].setValue('template name');
|
||||
component.networkAdaptersForm.controls['adapters'].setValue(1);
|
||||
component.server = {id: 1} as Server;
|
||||
it('should display correct label at start', async(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let selectedLabel = fixture.nativeElement
|
||||
.querySelector('[aria-selected="true"]');
|
||||
|
||||
component.addTemplate();
|
||||
expect(selectedLabel.textContent).toMatch('Server type');
|
||||
});
|
||||
}));
|
||||
|
||||
expect(mockedDockerService.addTemplate).toHaveBeenCalled();
|
||||
});
|
||||
it('should not call add template when required fields are empty', async(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let addButton = fixture.debugElement.nativeElement
|
||||
.querySelector('.add-button');
|
||||
spyOn(mockedDockerService, 'addTemplate').and.returnValue(of({} as DockerTemplate));
|
||||
|
||||
addButton.click();
|
||||
|
||||
it('should not call add template when file name is missing', () => {
|
||||
spyOn(mockedDockerService, 'addTemplate').and.returnValue(of({} as DockerTemplate));
|
||||
component.containerNameForm.controls['templateName'].setValue('template name');
|
||||
component.networkAdaptersForm.controls['adapters'].setValue(1);
|
||||
component.server = {id: 1} as Server;
|
||||
expect(component.virtualMachineForm.invalid).toBe(true);
|
||||
expect(component.containerNameForm.invalid).toBe(true);
|
||||
expect(component.networkAdaptersForm.invalid).toBe(true);
|
||||
|
||||
expect(mockedDockerService.addTemplate).not.toHaveBeenCalled();
|
||||
});
|
||||
}));
|
||||
|
||||
component.addTemplate();
|
||||
it('should call add template when required fields are filled', async(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let stepperComponent = fixture.debugElement
|
||||
.query(By.css('mat-vertical-stepper')).componentInstance;
|
||||
stepperComponent.selectedIndex = 1;
|
||||
component.newImageSelected = true;
|
||||
|
||||
expect(mockedDockerService.addTemplate).not.toHaveBeenCalled();
|
||||
});
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
let selectedLabel = fixture.nativeElement
|
||||
.querySelector('[aria-selected="true"]');
|
||||
|
||||
it('should not call add template when template name is missing', () => {
|
||||
spyOn(mockedDockerService, 'addTemplate').and.returnValue(of({} as DockerTemplate));
|
||||
component.virtualMachineForm.controls['filename'].setValue('sample name');
|
||||
component.networkAdaptersForm.controls['adapters'].setValue(1);
|
||||
component.server = {id: 1} as Server;
|
||||
expect(selectedLabel.textContent).toMatch('Docker Virtual Machine');
|
||||
|
||||
component.addTemplate();
|
||||
let filenameInput = fixture.debugElement.nativeElement
|
||||
.querySelector('.filename');
|
||||
filenameInput.value = 'sample filename';
|
||||
filenameInput.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.dockerTemplate.image).toBe('sample filename');
|
||||
|
||||
expect(mockedDockerService.addTemplate).not.toHaveBeenCalled();
|
||||
});
|
||||
expect(component.virtualMachineForm.invalid).toBe(false);
|
||||
expect(component.containerNameForm.invalid).toBe(true);
|
||||
|
||||
it('should not call add template when adapters field is empty', () => {
|
||||
spyOn(mockedDockerService, 'addTemplate').and.returnValue(of({} as DockerTemplate));
|
||||
component.virtualMachineForm.controls['filename'].setValue('sample name');
|
||||
component.containerNameForm.controls['templateName'].setValue('template name');
|
||||
component.server = {id: 1} as Server;
|
||||
stepperComponent.selectedIndex = 2;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
selectedLabel = fixture.nativeElement
|
||||
.querySelector('[aria-selected="true"]');
|
||||
|
||||
component.addTemplate();
|
||||
expect(selectedLabel.textContent).toMatch('Container name');
|
||||
|
||||
expect(mockedDockerService.addTemplate).not.toHaveBeenCalled();
|
||||
});
|
||||
let templatenameInput = fixture.debugElement.nativeElement
|
||||
.querySelector('.templatename');
|
||||
templatenameInput.value = 'sample templatename';
|
||||
templatenameInput.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.dockerTemplate.name).toBe('sample templatename');
|
||||
|
||||
expect(component.virtualMachineForm.invalid).toBe(false);
|
||||
expect(component.containerNameForm.invalid).toBe(false);
|
||||
|
||||
stepperComponent.selectedIndex = 3;
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
selectedLabel = fixture.nativeElement
|
||||
.querySelector('[aria-selected="true"]');
|
||||
|
||||
expect(selectedLabel.textContent).toMatch('Network adapters');
|
||||
|
||||
let networkadapterInput = fixture.debugElement.nativeElement
|
||||
.querySelector('.networkadapter');
|
||||
networkadapterInput.value = 2;
|
||||
networkadapterInput.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(component.dockerTemplate.adapters).toBe(2);
|
||||
|
||||
expect(component.virtualMachineForm.invalid).toBe(false);
|
||||
expect(component.containerNameForm.invalid).toBe(false);
|
||||
expect(component.networkAdaptersForm.invalid).toBe(false);
|
||||
|
||||
let addButton = fixture.debugElement.nativeElement
|
||||
.querySelector('.add-button');
|
||||
spyOn(mockedDockerService, 'addTemplate').and.returnValue(of({} as DockerTemplate));
|
||||
|
||||
addButton.click();
|
||||
|
||||
expect(mockedDockerService.addTemplate).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
@ -85,7 +85,7 @@ export class AddDockerTemplateComponent implements OnInit {
|
||||
}
|
||||
|
||||
addTemplate() {
|
||||
if (!this.virtualMachineForm.invalid && !this.containerNameForm.invalid && !this.networkAdaptersForm.invalid) {
|
||||
if ((!this.virtualMachineForm.invalid || !this.newImageSelected) && !this.containerNameForm.invalid && !this.networkAdaptersForm.invalid) {
|
||||
this.dockerTemplate.template_id = uuid();
|
||||
|
||||
this.dockerService.addTemplate(this.server, this.dockerTemplate).subscribe((template: DockerTemplate) => {
|
||||
|
@ -25,7 +25,8 @@ export class MockedIosService {
|
||||
}
|
||||
}
|
||||
|
||||
describe('AddIosTemplateComponent', () => {
|
||||
//Tests disabled due to instability
|
||||
xdescribe('AddIosTemplateComponent', () => {
|
||||
let component: AddIosTemplateComponent;
|
||||
let fixture: ComponentFixture<AddIosTemplateComponent>;
|
||||
|
||||
@ -36,21 +37,35 @@ describe('AddIosTemplateComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatStepperModule, FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: IosService, useValue: mockedIosService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService },
|
||||
{ provide: IosConfigurationService, useClass: IosConfigurationService }
|
||||
],
|
||||
declarations: [
|
||||
AddIosTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
imports: [
|
||||
MatStepperModule,
|
||||
FormsModule,
|
||||
MatTableModule,
|
||||
MatAutocompleteModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
MatSelectModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
CommonModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes([{path: 'server/1/preferences/dynamips/templates', component: AddIosTemplateComponent}])
|
||||
],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: IosService, useValue: mockedIosService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService },
|
||||
{ provide: IosConfigurationService, useClass: IosConfigurationService }
|
||||
],
|
||||
declarations: [
|
||||
AddIosTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
|
@ -25,7 +25,8 @@ export class MockedIouService {
|
||||
}
|
||||
}
|
||||
|
||||
describe('AddIouTemplateComponent', () => {
|
||||
//Tests disabled due to instability
|
||||
xdescribe('AddIouTemplateComponent', () => {
|
||||
let component: AddIouTemplateComponent;
|
||||
let fixture: ComponentFixture<AddIouTemplateComponent>;
|
||||
|
||||
@ -36,19 +37,35 @@ describe('AddIouTemplateComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatStepperModule, FormsModule, MatTableModule, MatAutocompleteModule, MatFormFieldModule, MatInputModule, ReactiveFormsModule, MatSelectModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: IouService, useValue: mockedIouService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService },
|
||||
{ provide: IouConfigurationService, useClass: IouConfigurationService }
|
||||
],
|
||||
declarations: [
|
||||
AddIouTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
imports: [
|
||||
MatStepperModule,
|
||||
FormsModule,
|
||||
MatTableModule,
|
||||
MatAutocompleteModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
MatSelectModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
CommonModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes([{path: 'server/1/preferences/iou/templates', component: AddIouTemplateComponent}])
|
||||
],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: IouService, useValue: mockedIouService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService },
|
||||
{ provide: IouConfigurationService, useClass: IouConfigurationService }
|
||||
],
|
||||
declarations: [
|
||||
AddIouTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
|
@ -33,7 +33,8 @@ export class MockedQemuService {
|
||||
}
|
||||
}
|
||||
|
||||
describe('AddQemuVmTemplateComponent', () => {
|
||||
//Tests disabled due to instability
|
||||
xdescribe('AddQemuVmTemplateComponent', () => {
|
||||
let component: AddQemuVmTemplateComponent;
|
||||
let fixture: ComponentFixture<AddQemuVmTemplateComponent>;
|
||||
|
||||
@ -47,16 +48,28 @@ describe('AddQemuVmTemplateComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatStepperModule, FormsModule, ReactiveFormsModule, MatSelectModule, MatAutocompleteModule, MatIconModule, MatFormFieldModule, MatInputModule,
|
||||
MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
imports: [
|
||||
MatStepperModule,
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatSelectModule,
|
||||
MatAutocompleteModule,
|
||||
MatIconModule,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
MatToolbarModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
CommonModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes([{path: 'server/1/preferences/qemu/templates', component: AddQemuVmTemplateComponent}])
|
||||
],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: Router, useValue: router },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: QemuService, useValue: mockedQemuService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: ToasterService, useValue: mockedToasterService },
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService },
|
||||
{ provide: QemuConfigurationService, useClass: QemuConfigurationService }
|
||||
],
|
||||
|
@ -11,7 +11,7 @@ import { ServerService } from '../../../../services/server.service';
|
||||
import { Server } from '../../../../models/server';
|
||||
import { MockedToasterService } from '../../../../services/toaster.service.spec';
|
||||
import { ToasterService } from '../../../../services/toaster.service';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { FormsModule, ReactiveFormsModule, AbstractControlDirective, FormControl } from '@angular/forms';
|
||||
import { MockedActivatedRoute } from '../../preferences.component.spec';
|
||||
import { QemuTemplate } from '../../../../models/templates/qemu-template';
|
||||
import { QemuVmTemplateDetailsComponent } from './qemu-vm-template-details.component';
|
||||
@ -49,13 +49,12 @@ describe('QemuVmTemplateDetailsComponent', () => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormsModule, ReactiveFormsModule, MatTableModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: QemuService, useValue: mockedQemuService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: QemuConfigurationService, useClass: QemuConfigurationService }
|
||||
{ provide: QemuConfigurationService, useClass: QemuConfigurationService },
|
||||
{ provide: AbstractControlDirective, useExisting: FormControl, useMulti: true }
|
||||
],
|
||||
declarations: [
|
||||
QemuVmTemplateDetailsComponent
|
||||
|
@ -39,20 +39,28 @@ describe('AddVirtualBoxTemplateComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: VirtualBoxService, useValue: mockedVirtualBoxService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
AddVirtualBoxTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
imports: [
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
CommonModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes([{path: 'server/1/preferences/virtualbox/templates', component: AddVirtualBoxTemplateComponent}])
|
||||
],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: VirtualBoxService, useValue: mockedVirtualBoxService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService },
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
AddVirtualBoxTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
|
@ -40,20 +40,28 @@ describe('AddVmwareTemplateComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: VmwareService, useValue: mockedVmwareService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
AddVmwareTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
imports: [
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
CommonModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes([{path: 'server/1/preferences/vmware/templates', component: AddVmwareTemplateComponent}])
|
||||
],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: VmwareService, useValue: mockedVmwareService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService },
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
AddVmwareTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
<mat-card class="matCard">
|
||||
<form [formGroup]="templateNameForm">
|
||||
<mat-form-field class="form-field">
|
||||
<input matInput formControlName="templateName" type="text" [(ngModel)]="templateName" placeholder="Template name">
|
||||
<input matInput formControlName="templateName" type="text" placeholder="Template name">
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</mat-card>
|
||||
|
@ -35,20 +35,28 @@ describe('AddVpcsTemplateComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule, RouterTestingModule.withRoutes([])],
|
||||
providers: [
|
||||
{
|
||||
provide: ActivatedRoute, useValue: activatedRoute
|
||||
},
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: VpcsService, useValue: mockedVpcsService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService},
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
AddVpcsTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
imports: [
|
||||
FormsModule,
|
||||
ReactiveFormsModule,
|
||||
MatIconModule,
|
||||
MatToolbarModule,
|
||||
MatMenuModule,
|
||||
MatCheckboxModule,
|
||||
CommonModule,
|
||||
NoopAnimationsModule,
|
||||
RouterTestingModule.withRoutes([{path: 'server/1/preferences/vpcs/templates', component: AddVpcsTemplateComponent}])
|
||||
],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: activatedRoute },
|
||||
{ provide: ServerService, useValue: mockedServerService },
|
||||
{ provide: VpcsService, useValue: mockedVpcsService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService },
|
||||
{ provide: TemplateMocksService, useClass: TemplateMocksService }
|
||||
],
|
||||
declarations: [
|
||||
AddVpcsTemplateComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
|
@ -47,6 +47,8 @@ export class AddVpcsTemplateComponent implements OnInit {
|
||||
|
||||
addTemplate() {
|
||||
if (!this.templateNameForm.invalid) {
|
||||
this.templateName = this.templateNameForm.get('templateName').value;
|
||||
|
||||
let vpcsTemplate: VpcsTemplate;
|
||||
|
||||
this.templateMocksService.getVpcsTemplate().subscribe((template: VpcsTemplate) => {
|
||||
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item (click)="openPacketFilters()">
|
||||
<mat-icon>filter_list</mat-icon>
|
||||
<span>Packet filters</span>
|
||||
</button>
|
@ -0,0 +1,30 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Link } from '../../../../../models/link';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Project } from '../../../../../models/project';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { PacketFiltersDialogComponent } from '../../../packet-capturing/packet-filters/packet-filters.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-packet-filters-action',
|
||||
templateUrl: './packet-filters-action.component.html'
|
||||
})
|
||||
export class PacketFiltersActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() project: Project;
|
||||
@Input() link: Link;
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
|
||||
openPacketFilters() {
|
||||
const dialogRef = this.dialog.open(PacketFiltersDialogComponent, {
|
||||
width: '900px',
|
||||
height: '400px',
|
||||
autoFocus: false
|
||||
});
|
||||
let instance = dialogRef.componentInstance;
|
||||
instance.server = this.server;
|
||||
instance.project = this.project;
|
||||
instance.link = this.link;
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item *ngIf="link.suspend" (click)="resumeLink()">
|
||||
<mat-icon>play_arrow</mat-icon>
|
||||
<span>Resume</span>
|
||||
</button>
|
@ -0,0 +1,22 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Link } from '../../../../../models/link';
|
||||
import { LinkService } from '../../../../../services/link.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-resume-link-action',
|
||||
templateUrl: './resume-link-action.component.html'
|
||||
})
|
||||
export class ResumeLinkActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() link: Link;
|
||||
|
||||
constructor(
|
||||
private linkService: LinkService
|
||||
) {}
|
||||
|
||||
resumeLink() {
|
||||
this.link.suspend = false;
|
||||
this.linkService.updateLink(this.server, this.link).subscribe(() => {});
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item *ngIf="!link.capturing" (click)="startCapture()">
|
||||
<mat-icon>loupe</mat-icon>
|
||||
<span>Start capture</span>
|
||||
</button>
|
@ -0,0 +1,26 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Link } from '../../../../../models/link';
|
||||
import { MatDialog } from '@angular/material';
|
||||
import { StartCaptureDialogComponent } from '../../../packet-capturing/start-capture/start-capture.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-start-capture-action',
|
||||
templateUrl: './start-capture-action.component.html'
|
||||
})
|
||||
export class StartCaptureActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() link: Link;
|
||||
|
||||
constructor(private dialog: MatDialog) {}
|
||||
|
||||
startCapture() {
|
||||
const dialogRef = this.dialog.open(StartCaptureDialogComponent, {
|
||||
width: '400px',
|
||||
autoFocus: false
|
||||
});
|
||||
let instance = dialogRef.componentInstance;
|
||||
instance.server = this.server;
|
||||
instance.link = this.link;
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Component, Input, OnInit, OnChanges } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
@ -7,7 +7,7 @@ import { Node } from '../../../../../cartography/models/node';
|
||||
selector: 'app-start-node-action',
|
||||
templateUrl: './start-node-action.component.html'
|
||||
})
|
||||
export class StartNodeActionComponent implements OnInit {
|
||||
export class StartNodeActionComponent implements OnInit, OnChanges {
|
||||
@Input() server: Server;
|
||||
@Input() nodes: Node[];
|
||||
isNodeWithStoppedStatus: boolean;
|
||||
@ -15,11 +15,17 @@ export class StartNodeActionComponent implements OnInit {
|
||||
constructor(private nodeService: NodeService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.nodes.forEach((node) => {
|
||||
if (node.status === 'stopped') {
|
||||
this.isNodeWithStoppedStatus = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes) {
|
||||
if(changes.nodes) {
|
||||
this.isNodeWithStoppedStatus = false;
|
||||
this.nodes.forEach((node) => {
|
||||
if (node.status === 'stopped') {
|
||||
this.isNodeWithStoppedStatus = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
startNodes() {
|
||||
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item *ngIf="link.capturing" (click)="stopCapture()">
|
||||
<mat-icon>pause_circle_filled</mat-icon>
|
||||
<span>Stop capture</span>
|
||||
</button>
|
@ -0,0 +1,21 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Link } from '../../../../../models/link';
|
||||
import { LinkService } from '../../../../../services/link.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-stop-capture-action',
|
||||
templateUrl: './stop-capture-action.component.html'
|
||||
})
|
||||
export class StopCaptureActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() link: Link;
|
||||
|
||||
constructor(
|
||||
private linkService: LinkService
|
||||
) {}
|
||||
|
||||
stopCapture() {
|
||||
this.linkService.stopCaptureOnLink(this.server, this.link).subscribe(() => {});
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Component, Input, OnInit } from '@angular/core';
|
||||
import { Component, Input, OnInit, OnChanges } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { NodeService } from '../../../../../services/node.service';
|
||||
import { Node } from '../../../../../cartography/models/node';
|
||||
@ -7,7 +7,7 @@ import { Node } from '../../../../../cartography/models/node';
|
||||
selector: 'app-stop-node-action',
|
||||
templateUrl: './stop-node-action.component.html'
|
||||
})
|
||||
export class StopNodeActionComponent implements OnInit {
|
||||
export class StopNodeActionComponent implements OnInit, OnChanges {
|
||||
@Input() server: Server;
|
||||
@Input() nodes: Node[];
|
||||
isNodeWithStartedStatus: boolean;
|
||||
@ -15,11 +15,17 @@ export class StopNodeActionComponent implements OnInit {
|
||||
constructor(private nodeService: NodeService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.nodes.forEach((node) => {
|
||||
if (node.status === 'started') {
|
||||
this.isNodeWithStartedStatus = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes) {
|
||||
if(changes.nodes) {
|
||||
this.isNodeWithStartedStatus = false;
|
||||
this.nodes.forEach((node) => {
|
||||
if (node.status === 'started') {
|
||||
this.isNodeWithStartedStatus = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
stopNodes() {
|
||||
|
@ -0,0 +1,4 @@
|
||||
<button mat-menu-item *ngIf="!link.suspend" (click)="suspendLink()">
|
||||
<mat-icon>pause</mat-icon>
|
||||
<span>Suspend</span>
|
||||
</button>
|
@ -0,0 +1,22 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { Server } from '../../../../../models/server';
|
||||
import { Link } from '../../../../../models/link';
|
||||
import { LinkService } from '../../../../../services/link.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-suspend-link-action',
|
||||
templateUrl: './suspend-link-action.component.html'
|
||||
})
|
||||
export class SuspendLinkActionComponent {
|
||||
@Input() server: Server;
|
||||
@Input() link: Link;
|
||||
|
||||
constructor(
|
||||
private linkService: LinkService
|
||||
) {}
|
||||
|
||||
suspendLink() {
|
||||
this.link.suspend = true;
|
||||
this.linkService.updateLink(this.server, this.link).subscribe(() => {});
|
||||
}
|
||||
}
|
@ -32,6 +32,34 @@
|
||||
[nodes]="nodes"
|
||||
[drawings]="drawings"
|
||||
></app-move-layer-down-action>
|
||||
<app-start-capture-action
|
||||
*ngIf="!projectService.isReadOnly(project) && isBundledServer
|
||||
&& drawings.length===0 && nodes.length===0 && links.length===1"
|
||||
[server]="server"
|
||||
[link]="links[0]"
|
||||
></app-start-capture-action>
|
||||
<app-stop-capture-action
|
||||
*ngIf="!projectService.isReadOnly(project) && isBundledServer
|
||||
&& drawings.length===0 && nodes.length===0 && links.length===1"
|
||||
[server]="server"
|
||||
[link]="links[0]"
|
||||
></app-stop-capture-action>
|
||||
<app-packet-filters-action
|
||||
*ngIf="!projectService.isReadOnly(project) && drawings.length===0 && nodes.length===0 && links.length===1"
|
||||
[server]="server"
|
||||
[project]="project"
|
||||
[link]="links[0]"
|
||||
></app-packet-filters-action>
|
||||
<app-resume-link-action
|
||||
*ngIf="!projectService.isReadOnly(project) && drawings.length===0 && nodes.length===0 && links.length===1"
|
||||
[server]="server"
|
||||
[link]="links[0]"
|
||||
></app-resume-link-action>
|
||||
<app-suspend-link-action
|
||||
*ngIf="!projectService.isReadOnly(project) && drawings.length===0 && nodes.length===0 && links.length===1"
|
||||
[server]="server"
|
||||
[link]="links[0]"
|
||||
></app-suspend-link-action>
|
||||
<app-delete-action
|
||||
*ngIf="!projectService.isReadOnly(project)"
|
||||
[server]="server"
|
||||
|
@ -8,6 +8,7 @@ import { MatMenuModule, MatMenuTrigger } from '@angular/material';
|
||||
import { Drawing } from '../../../cartography/models/drawing';
|
||||
import { RectElement } from '../../../cartography/models/drawings/rect-element';
|
||||
import { TextElement } from '../../../cartography/models/drawings/text-element';
|
||||
import { Server } from '../../../models/server';
|
||||
|
||||
describe('ContextMenuComponent', () => {
|
||||
let component: ContextMenuComponent;
|
||||
@ -28,6 +29,7 @@ describe('ContextMenuComponent', () => {
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ContextMenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.server = {location: 'local'} as Server;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
@ -33,6 +33,7 @@ export class ContextMenuComponent implements OnInit {
|
||||
|
||||
hasTextCapabilities = false;
|
||||
isElectronApp = false;
|
||||
isBundledServer: boolean = false;
|
||||
|
||||
constructor(
|
||||
private sanitizer: DomSanitizer,
|
||||
@ -43,7 +44,9 @@ export class ContextMenuComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.setPosition(0, 0);
|
||||
|
||||
this.isElectronApp = this.electronService.isElectronApp;
|
||||
this.isBundledServer = this.server.location === 'bundled';
|
||||
}
|
||||
|
||||
public setPosition(top: number, left: number) {
|
||||
|
@ -9,6 +9,8 @@ import { MapPort } from '../../../cartography/models/map/map-port';
|
||||
import { MapLinkCreated } from '../../../cartography/events/links';
|
||||
import { Link } from '../../../models/link';
|
||||
import { MapNodeToNodeConverter } from '../../../cartography/converters/map/map-node-to-node-converter';
|
||||
import { PortToMapPortConverter } from '../../../cartography/converters/map/port-to-map-port-converter';
|
||||
import { NodeToMapNodeConverter } from '../../../cartography/converters/map/node-to-map-node-converter';
|
||||
|
||||
@Component({
|
||||
selector: 'app-draw-link-tool',
|
||||
@ -25,7 +27,9 @@ export class DrawLinkToolComponent implements OnInit, OnDestroy {
|
||||
private drawingLineTool: DrawingLineWidget,
|
||||
private nodesEventSource: NodesEventSource,
|
||||
private linksEventSource: LinksEventSource,
|
||||
private mapNodeToNode: MapNodeToNodeConverter
|
||||
private mapNodeToNode: MapNodeToNodeConverter,
|
||||
private nodeToMapNode: NodeToMapNodeConverter,
|
||||
private portToMapPort: PortToMapPortConverter
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@ -43,8 +47,8 @@ export class DrawLinkToolComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
public onChooseInterface(event) {
|
||||
const node: MapNode = event.node;
|
||||
const port: MapPort = event.port;
|
||||
const node: MapNode = this.nodeToMapNode.convert(event.node);
|
||||
const port: MapPort = this.portToMapPort.convert(event.port);
|
||||
if (this.drawingLineTool.isDrawing()) {
|
||||
const data = this.drawingLineTool.stop();
|
||||
this.linksEventSource.created.emit(new MapLinkCreated(data['node'], data['port'], node, port));
|
||||
|
@ -0,0 +1,16 @@
|
||||
<h1 mat-dialog-title>{{title}}</h1>
|
||||
|
||||
<div class="modal-form-container">
|
||||
<div class="message" *ngFor="let message of messages; let i = index">
|
||||
<h6>
|
||||
{{message.name}}
|
||||
</h6>
|
||||
<span class="description">
|
||||
{{message.description}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button (click)="onCloseClick()" color="accent">Close</button>
|
||||
</div>
|
@ -0,0 +1,7 @@
|
||||
.message {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.description {
|
||||
color: #b0bec5;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { MatDialogRef } from '@angular/material';
|
||||
import { Message } from '../../../models/message';
|
||||
|
||||
@Component({
|
||||
selector: 'app-help-dialog',
|
||||
templateUrl: './help-dialog.component.html',
|
||||
styleUrls: ['./help-dialog.component.scss']
|
||||
})
|
||||
export class HelpDialogComponent {
|
||||
@Input() title: string;
|
||||
@Input() messages: Message[];
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<HelpDialogComponent>,
|
||||
) {}
|
||||
|
||||
onCloseClick() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
<button
|
||||
matTooltip="Start/Resume all nodes"
|
||||
mat-icon-button
|
||||
(click)="startNodes()"
|
||||
class="menu-button"
|
||||
>
|
||||
<mat-icon>play_arrow</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Suspend all nodes"
|
||||
mat-icon-button
|
||||
(click)="suspendNodes()"
|
||||
class="menu-button"
|
||||
>
|
||||
<mat-icon>pause</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Stop all nodes"
|
||||
mat-icon-button
|
||||
(click)="stopNodes()"
|
||||
class="menu-button"
|
||||
>
|
||||
<mat-icon>stop</mat-icon>
|
||||
</button>
|
||||
<button
|
||||
matTooltip="Reload all nodes"
|
||||
mat-icon-button
|
||||
(click)="reloadNodes()"
|
||||
class="menu-button"
|
||||
>
|
||||
<mat-icon>replay</mat-icon>
|
||||
</button>
|
@ -0,0 +1,12 @@
|
||||
.menu-button {
|
||||
outline: 0 !important;
|
||||
transition: 0.5s;
|
||||
margin-bottom: 16px;
|
||||
width: 40px;
|
||||
margin-right: 12px !important;
|
||||
margin-left: 12px !important;
|
||||
background: #263238;
|
||||
padding: 0;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NodesMenuComponent } from './nodes-menu.component';
|
||||
import { MockedToasterService } from '../../../services/toaster.service.spec';
|
||||
import { MockedNodeService } from '../project-map.component.spec';
|
||||
import { MatButtonModule, MatIconModule } from '@angular/material';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { NodeService } from '../../../services/node.service';
|
||||
import { ToasterService } from '../../../services/toaster.service';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
describe('NodesMenuComponent', () => {
|
||||
let component: NodesMenuComponent;
|
||||
let fixture: ComponentFixture<NodesMenuComponent>;
|
||||
let mockedToasterService: MockedToasterService = new MockedToasterService();
|
||||
let mockedNodeService: MockedNodeService = new MockedNodeService();
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatButtonModule, MatIconModule, CommonModule, NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: NodeService, useValue: mockedNodeService },
|
||||
{ provide: ToasterService, useValue: mockedToasterService }
|
||||
],
|
||||
declarations: [
|
||||
NodesMenuComponent,
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(NodesMenuComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should call start all nodes', () => {
|
||||
spyOn(mockedNodeService, 'startAll').and.returnValue(of());
|
||||
|
||||
component.startNodes();
|
||||
|
||||
expect(mockedNodeService.startAll).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call stop all nodes', () => {
|
||||
spyOn(mockedNodeService, 'stopAll').and.returnValue(of());
|
||||
|
||||
component.stopNodes();
|
||||
|
||||
expect(mockedNodeService.stopAll).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call suspend all nodes', () => {
|
||||
spyOn(mockedNodeService, 'suspendAll').and.returnValue(of());
|
||||
|
||||
component.suspendNodes();
|
||||
|
||||
expect(mockedNodeService.suspendAll).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call reload all nodes', () => {
|
||||
spyOn(mockedNodeService, 'reloadAll').and.returnValue(of());
|
||||
|
||||
component.reloadNodes();
|
||||
|
||||
expect(mockedNodeService.reloadAll).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -0,0 +1,44 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
import { Project } from '../../../models/project';
|
||||
import { Server } from '../../../models/server';
|
||||
import { NodeService } from '../../../services/node.service';
|
||||
import { ToasterService } from '../../../services/toaster.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-nodes-menu',
|
||||
templateUrl: './nodes-menu.component.html',
|
||||
styleUrls: ['./nodes-menu.component.scss']
|
||||
})
|
||||
export class NodesMenuComponent {
|
||||
@Input('project') project: Project;
|
||||
@Input('server') server: Server;
|
||||
|
||||
constructor(
|
||||
private nodeService: NodeService,
|
||||
private toasterService: ToasterService
|
||||
) {}
|
||||
|
||||
startNodes() {
|
||||
this.nodeService.startAll(this.server, this.project).subscribe(() => {
|
||||
this.toasterService.success('All nodes successfully started');
|
||||
});
|
||||
}
|
||||
|
||||
stopNodes() {
|
||||
this.nodeService.stopAll(this.server, this.project).subscribe(() => {
|
||||
this.toasterService.success('All nodes successfully stopped');
|
||||
});
|
||||
}
|
||||
|
||||
suspendNodes() {
|
||||
this.nodeService.suspendAll(this.server, this.project).subscribe(() => {
|
||||
this.toasterService.success('All nodes successfully suspended');
|
||||
});
|
||||
}
|
||||
|
||||
reloadNodes() {
|
||||
this.nodeService.reloadAll(this.server, this.project).subscribe(() => {
|
||||
this.toasterService.success('All nodes successfully reloaded');
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
<h1 mat-dialog-title>Packet filters</h1>
|
||||
|
||||
<div class="modal-form-container" class="content">
|
||||
<mat-tab-group *ngIf="this.filters">
|
||||
<mat-tab label="Frequency drop">
|
||||
<mat-form-field class="input-field">
|
||||
<input matInput placeholder="Frequency" type="number" [(ngModel)]="filters.frequency_drop[0]">
|
||||
</mat-form-field>
|
||||
</mat-tab>
|
||||
<mat-tab label="Packet loss">
|
||||
<mat-form-field class="input-field">
|
||||
<input matInput placeholder="Chance" type="number" [(ngModel)]="filters.packet_loss[0]">
|
||||
</mat-form-field>
|
||||
</mat-tab>
|
||||
<mat-tab label="Delay">
|
||||
<mat-form-field class="input-field">
|
||||
<input matInput placeholder="Latency" type="number" [(ngModel)]="filters.delay[0]">
|
||||
</mat-form-field>
|
||||
<mat-form-field class="input-field">
|
||||
<input matInput placeholder="Jitter" type="number" [(ngModel)]="filters.delay[1]">
|
||||
</mat-form-field>
|
||||
</mat-tab>
|
||||
<mat-tab label="Corrupt">
|
||||
<mat-form-field class="input-field">
|
||||
<input matInput placeholder="Latency" type="number" [(ngModel)]="filters.corrupt[0]">
|
||||
</mat-form-field>
|
||||
</mat-tab>
|
||||
<mat-tab label="Berkeley Packet Filter (BPF)">
|
||||
<mat-form-field class="input-field">
|
||||
<textarea matInput type="text" [(ngModel)]="filters.bpf[0]"></textarea>
|
||||
</mat-form-field>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
</div>
|
||||
<div class="bottom-bar">
|
||||
<div class="spacer"></div>
|
||||
<div mat-dialog-actions layout="row" class="dialog-actions">
|
||||
<button mat-button (click)="onNoClick()" color="accent">Cancel</button>
|
||||
<button mat-button (click)="onResetClick()" color="accent">Reset</button>
|
||||
<button mat-button (click)="onYesClick()" tabindex="2" mat-raised-button color="primary">Apply</button>
|
||||
<div class="divider"></div>
|
||||
<button mat-button (click)="onHelpClick()" color="accent">Help</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,67 @@
|
||||
.spacer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.content {
|
||||
height: 260px;
|
||||
}
|
||||
|
||||
.item {
|
||||
height: 25px;
|
||||
font-size: 10pt;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.item-name {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.input-field {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.divider {
|
||||
width: fit-content;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.input-color {
|
||||
padding: 0px;
|
||||
border-width: 0px;
|
||||
width: 100%;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type="color"] {
|
||||
-webkit-appearance: none;
|
||||
border: none;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
input[type="color"]::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type="color"]::-webkit-color-swatch {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-form-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modal-form-container > * {
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { LinkService } from '../../../../services/link.service';
|
||||
import { MockedLinkService } from '../../project-map.component.spec';
|
||||
import { Link } from '../../../../models/link';
|
||||
import { of } from 'rxjs';
|
||||
import { PacketFiltersDialogComponent } from './packet-filters.component';
|
||||
|
||||
describe('PacketFiltersDialogComponent', () => {
|
||||
let component: PacketFiltersDialogComponent;
|
||||
let fixture: ComponentFixture<PacketFiltersDialogComponent>;
|
||||
|
||||
let mockedLinkService = new MockedLinkService;
|
||||
let dialogRef = {
|
||||
close: jasmine.createSpy('close')
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatDialogModule, FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: [] },
|
||||
{ provide: LinkService, useValue: mockedLinkService }
|
||||
],
|
||||
declarations: [
|
||||
PacketFiltersDialogComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(PacketFiltersDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.link = {link_type: 'ethernet'} as Link;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should call update link when filters applied', () => {
|
||||
spyOn(mockedLinkService, 'updateLink').and.returnValue(of({}));
|
||||
|
||||
component.onYesClick();
|
||||
|
||||
expect(mockedLinkService.updateLink).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call update link after resetting', () => {
|
||||
spyOn(mockedLinkService, 'updateLink').and.returnValue(of({}));
|
||||
|
||||
component.onResetClick();
|
||||
|
||||
expect(mockedLinkService.updateLink).toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -0,0 +1,96 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Link } from '../../../../models/link';
|
||||
import { Server } from '../../../../models/server';
|
||||
import { Project } from '../../../../models/project';
|
||||
import { MatDialogRef, MatDialog } from '@angular/material';
|
||||
import { LinkService } from '../../../../services/link.service';
|
||||
import { FilterDescription } from '../../../../models/filter-description';
|
||||
import { HelpDialogComponent } from '../../help-dialog/help-dialog.component';
|
||||
import { Message } from '../../../../models/message';
|
||||
import { Filter } from '../../../../models/filter';
|
||||
|
||||
@Component({
|
||||
selector: 'app-packet-filters',
|
||||
templateUrl: './packet-filters.component.html',
|
||||
styleUrls: ['./packet-filters.component.scss']
|
||||
})
|
||||
export class PacketFiltersDialogComponent implements OnInit{
|
||||
server: Server;
|
||||
project: Project;
|
||||
link: Link;
|
||||
filters: Filter;
|
||||
availableFilters: FilterDescription[];
|
||||
|
||||
constructor(
|
||||
private dialogRef: MatDialogRef<PacketFiltersDialogComponent>,
|
||||
private linkService: LinkService,
|
||||
private dialog: MatDialog
|
||||
) {}
|
||||
|
||||
ngOnInit(){
|
||||
this.linkService.getLink(this.server, this.link.project_id, this.link.link_id).subscribe((link: Link) => {
|
||||
this.link = link;
|
||||
this.filters = {
|
||||
bpf: [],
|
||||
corrupt: [0],
|
||||
delay: [0, 0],
|
||||
frequency_drop: [0],
|
||||
packet_loss: [0]
|
||||
};
|
||||
|
||||
if (this.link.filters) {
|
||||
this.filters.bpf = this.link.filters.bpf ? this.link.filters.bpf : [];
|
||||
this.filters.corrupt = this.link.filters.corrupt ? this.link.filters.corrupt : [0];
|
||||
this.filters.delay = this.link.filters.delay ? this.link.filters.delay : [0, 0];
|
||||
this.filters.frequency_drop = this.link.filters.frequency_drop ? this.link.filters.frequency_drop : [0];
|
||||
this.filters.packet_loss = this.link.filters.packet_loss ? this.link.filters.packet_loss : [0];
|
||||
}
|
||||
});
|
||||
|
||||
this.linkService.getAvailableFilters(this.server, this.link).subscribe((availableFilters: FilterDescription[]) => {
|
||||
this.availableFilters = availableFilters;
|
||||
});
|
||||
}
|
||||
|
||||
onNoClick() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
|
||||
onResetClick() {
|
||||
this.link.filters = {
|
||||
bpf: [],
|
||||
corrupt: [0],
|
||||
delay: [0, 0],
|
||||
frequency_drop: [0],
|
||||
packet_loss: [0]
|
||||
};
|
||||
|
||||
this.linkService.updateLink(this.server, this.link).subscribe((link: Link) => {
|
||||
this.dialogRef.close();
|
||||
});
|
||||
}
|
||||
|
||||
onYesClick() {
|
||||
this.link.filters = this.filters;
|
||||
this.linkService.updateLink(this.server, this.link).subscribe((link: Link) => {
|
||||
this.dialogRef.close();
|
||||
});
|
||||
}
|
||||
|
||||
onHelpClick() {
|
||||
const dialogRef = this.dialog.open(HelpDialogComponent, {
|
||||
width: '500px',
|
||||
autoFocus: false
|
||||
});
|
||||
let instance = dialogRef.componentInstance;
|
||||
instance.title = 'Help for filters';
|
||||
let messages: Message[] = [];
|
||||
this.availableFilters.forEach((filter: FilterDescription) => {
|
||||
messages.push({
|
||||
name: filter.name,
|
||||
description: filter.description
|
||||
});
|
||||
});
|
||||
instance.messages = messages;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
<h1 mat-dialog-title>Packet capture</h1>
|
||||
|
||||
<div class="modal-form-container">
|
||||
<form [formGroup]="inputForm">
|
||||
<mat-form-field class="input-field">
|
||||
<mat-select
|
||||
placeholder="Link type"
|
||||
formControlName="linkType"
|
||||
ngDefaultControl>
|
||||
<mat-option *ngFor="let type of linkTypes" [value]="type[1]">
|
||||
{{type[0]}}
|
||||
</mat-option>
|
||||
</mat-select>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="input-field">
|
||||
<input
|
||||
placeholder="File name"
|
||||
formControlName="fileName"
|
||||
matInput type="text">
|
||||
</mat-form-field>
|
||||
<!-- <mat-checkbox [ngModelOptions]="{standalone: true}" [(ngModel)]="startProgram">
|
||||
Start the capture visualization program
|
||||
</mat-checkbox> -->
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button (click)="onNoClick()" color="accent">Cancel</button>
|
||||
<button mat-button (click)="onYesClick()" tabindex="2" mat-raised-button color="primary">Ok</button>
|
||||
</div>
|
@ -0,0 +1,3 @@
|
||||
.input-field {
|
||||
width: 100%;
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
import { StartCaptureDialogComponent } from "./start-capture.component";
|
||||
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { ToasterService } from '../../../../services/toaster.service';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { MockedToasterService } from '../../../../services/toaster.service.spec';
|
||||
import { LinkService } from '../../../../services/link.service';
|
||||
import { MockedLinkService, MockedNodesDataSource } from '../../project-map.component.spec';
|
||||
import { Link } from '../../../../models/link';
|
||||
import { of } from 'rxjs';
|
||||
import { NodesDataSource } from '../../../../cartography/datasources/nodes-datasource';
|
||||
|
||||
describe('StartCaptureDialogComponent', () => {
|
||||
let component: StartCaptureDialogComponent;
|
||||
let fixture: ComponentFixture<StartCaptureDialogComponent>;
|
||||
|
||||
let mockedToasterService = new MockedToasterService;
|
||||
let mockedLinkService = new MockedLinkService;
|
||||
let mockedNodesDataSource = new MockedNodesDataSource;
|
||||
let dialogRef = {
|
||||
close: jasmine.createSpy('close')
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [MatDialogModule, FormsModule, ReactiveFormsModule, MatIconModule, MatToolbarModule, MatMenuModule, MatCheckboxModule, CommonModule, NoopAnimationsModule],
|
||||
providers: [
|
||||
{ provide: MatDialogRef, useValue: dialogRef },
|
||||
{ provide: MAT_DIALOG_DATA, useValue: [] },
|
||||
{ provide: ToasterService, useValue: mockedToasterService },
|
||||
{ provide: LinkService, useValue: mockedLinkService },
|
||||
{ provide: NodesDataSource, useValue: mockedNodesDataSource }
|
||||
],
|
||||
declarations: [
|
||||
StartCaptureDialogComponent
|
||||
],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(StartCaptureDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.link = {link_type: 'ethernet', nodes: [{node_id: '1'}, {node_id: '2'}]} as Link;
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should call link service when input is valid', () => {
|
||||
component.inputForm.controls['linkType'].setValue('Ethernet');
|
||||
component.inputForm.controls['fileName'].setValue('SampleFileName');
|
||||
spyOn(mockedLinkService, 'startCaptureOnLink').and.returnValue(of({}));
|
||||
|
||||
component.onYesClick();
|
||||
|
||||
expect(mockedLinkService.startCaptureOnLink).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call link service when link type is not set', () => {
|
||||
component.inputForm.controls['fileName'].setValue('SampleFileName');
|
||||
spyOn(mockedLinkService, 'startCaptureOnLink').and.returnValue(of({}));
|
||||
|
||||
component.onYesClick();
|
||||
|
||||
expect(mockedLinkService.startCaptureOnLink).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call link service when filename is empty', () => {
|
||||
component.inputForm.controls['linkType'].setValue('Ethernet');
|
||||
component.inputForm.controls['fileName'].setValue('');
|
||||
spyOn(mockedLinkService, 'startCaptureOnLink').and.returnValue(of({}));
|
||||
|
||||
component.onYesClick();
|
||||
|
||||
expect(mockedLinkService.startCaptureOnLink).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
@ -0,0 +1,85 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Server } from '../../../../models/server';
|
||||
import { Link } from '../../../../models/link';
|
||||
import { MatDialogRef } from '@angular/material';
|
||||
import { PacketFiltersDialogComponent } from '../packet-filters/packet-filters.component';
|
||||
import { LinkService } from '../../../../services/link.service';
|
||||
import { CapturingSettings } from '../../../../models/capturingSettings';
|
||||
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
|
||||
import { ToasterService } from '../../../../services/toaster.service';
|
||||
import { LinkNode } from '../../../../models/link-node';
|
||||
import { NodesDataSource } from '../../../../cartography/datasources/nodes-datasource';
|
||||
|
||||
@Component({
|
||||
selector: 'app-start-capture',
|
||||
templateUrl: './start-capture.component.html',
|
||||
styleUrls: ['./start-capture.component.scss']
|
||||
})
|
||||
export class StartCaptureDialogComponent implements OnInit {
|
||||
server: Server;
|
||||
link: Link;
|
||||
linkTypes = [];
|
||||
inputForm: FormGroup;
|
||||
startProgram: boolean;
|
||||
|
||||
constructor(
|
||||
private dialogRef: MatDialogRef<PacketFiltersDialogComponent>,
|
||||
private linkService: LinkService,
|
||||
private formBuilder: FormBuilder,
|
||||
private toasterService: ToasterService,
|
||||
private nodesDataSource: NodesDataSource
|
||||
) {
|
||||
this.inputForm = this.formBuilder.group({
|
||||
linkType: new FormControl('', Validators.required),
|
||||
fileName: new FormControl('', Validators.required)
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.link.link_type === 'ethernet') {
|
||||
this.linkTypes = [
|
||||
["Ethernet", "DLT_EN10MB"]
|
||||
];
|
||||
} else {
|
||||
this.linkTypes = [
|
||||
["Cisco HDLC", "DLT_C_HDLC"],
|
||||
["Cisco PPP", "DLT_PPP_SERIAL"],
|
||||
["Frame Relay", "DLT_FRELAY"],
|
||||
["ATM", "DLT_ATM_RFC1483"]
|
||||
];
|
||||
}
|
||||
|
||||
const sourceNode = this.nodesDataSource.get(this.link.nodes[0].node_id);
|
||||
const targetNode = this.nodesDataSource.get(this.link.nodes[1].node_id);
|
||||
const sourcePort = sourceNode.ports[this.link.nodes[0].port_number];
|
||||
const targetPort = targetNode.ports[this.link.nodes[1].port_number];
|
||||
this.inputForm.controls['fileName'].setValue(`${sourceNode.name}_${sourcePort.name}_to_${targetNode.name}_${targetPort.name}`);
|
||||
}
|
||||
|
||||
onYesClick() {
|
||||
let isAnyRunningDevice = false;
|
||||
this.link.nodes.forEach((linkNode: LinkNode) => {
|
||||
let node = this.nodesDataSource.get(linkNode.node_id);
|
||||
if (node.status === 'started') isAnyRunningDevice = true;
|
||||
});
|
||||
|
||||
if (!isAnyRunningDevice) {
|
||||
this.toasterService.error(`Cannot capture because there is no running device on this link`);
|
||||
} else if (this.inputForm.invalid) {
|
||||
this.toasterService.error(`Fill all required fields`);
|
||||
} else {
|
||||
let captureSettings: CapturingSettings = {
|
||||
capture_file_name: this.inputForm.get('fileName').value,
|
||||
data_link_type: this.inputForm.get('linkType').value
|
||||
};
|
||||
|
||||
this.linkService.startCaptureOnLink(this.server, this.link, captureSettings).subscribe(() => {
|
||||
this.dialogRef.close();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onNoClick() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@
|
||||
<div class="project-toolbar">
|
||||
<mat-toolbar color="primary" class="project-toolbar">
|
||||
<mat-toolbar-row>
|
||||
<button mat-icon-button [matMenuTriggerFor]="mainMenu"><mat-icon svgIcon="gns3"></mat-icon></button>
|
||||
<button matTooltip="Open menu" mat-icon-button [matMenuTriggerFor]="mainMenu"><mat-icon svgIcon="gns3"></mat-icon></button>
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-menu #mainMenu="matMenu" [overlapTrigger]="false">
|
||||
@ -48,7 +48,7 @@
|
||||
</mat-menu>
|
||||
|
||||
<mat-toolbar-row>
|
||||
<button mat-icon-button [matMenuTriggerFor]="viewMenu"><mat-icon>view_module</mat-icon></button>
|
||||
<button matTooltip="Show/hide interface labels" mat-icon-button [matMenuTriggerFor]="viewMenu"><mat-icon>view_module</mat-icon></button>
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-menu #viewMenu="matMenu" [overlapTrigger]="false">
|
||||
@ -60,13 +60,13 @@
|
||||
</mat-menu>
|
||||
|
||||
<mat-toolbar-row *ngIf="!readonly">
|
||||
<button mat-icon-button [color]="tools.draw_link ? 'primary' : 'basic'" (click)="toggleDrawLineMode()">
|
||||
<button matTooltip="Add a link" mat-icon-button [color]="tools.draw_link ? 'primary' : 'basic'" (click)="toggleDrawLineMode()">
|
||||
<mat-icon>timeline</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-toolbar-row>
|
||||
<button mat-icon-button [color]="tools.moving ? 'primary' : 'basic'" (click)="toggleMovingMode()">
|
||||
<button matTooltip="Enable/disable moving mode" mat-icon-button [color]="tools.moving ? 'primary' : 'basic'" (click)="toggleMovingMode()">
|
||||
<mat-icon>zoom_out_map</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar-row>
|
||||
@ -80,18 +80,20 @@
|
||||
</mat-toolbar-row>
|
||||
|
||||
<mat-toolbar-row *ngIf="!readonly">
|
||||
<button mat-icon-button routerLink="/server/{{server.id}}/preferences">
|
||||
<button matTooltip="Go to preferences" mat-icon-button routerLink="/server/{{server.id}}/preferences">
|
||||
<mat-icon>settings_applications</mat-icon>
|
||||
</button>
|
||||
</mat-toolbar-row>
|
||||
</mat-toolbar>
|
||||
</div>
|
||||
|
||||
<div id="show-menu-wrapper" [ngClass]="{ shadowed: !drawTools.visibility }">
|
||||
<div id="show-menu-wrapper" [ngClass]="{ shadowed: !drawTools.visibility }" *ngIf="!readonly">
|
||||
<button class="arrow-button" mat-icon-button (click)="showMenu()"><mat-icon>keyboard_arrow_right</mat-icon></button>
|
||||
</div>
|
||||
|
||||
<div id="menu-wrapper" [ngClass]="{ extended: drawTools.visibility }">
|
||||
<app-nodes-menu [server]="server" [project]="project"></app-nodes-menu>
|
||||
<mat-divider class="divider" [vertical]="true"></mat-divider>
|
||||
<button
|
||||
matTooltip="Add a note"
|
||||
mat-icon-button
|
||||
@ -119,7 +121,7 @@
|
||||
>
|
||||
<mat-icon>panorama_fish_eye</mat-icon>
|
||||
</button>
|
||||
<button matTooltip="Draw line" mat-icon-button class="menu-button" (click)="addDrawing('line')">
|
||||
<button matTooltip="Draw a line" mat-icon-button class="menu-button" (click)="addDrawing('line')">
|
||||
<svg height="40" width="40">
|
||||
<line
|
||||
[ngClass]="{ selected: drawTools.isLineChosen }"
|
||||
|
@ -53,6 +53,7 @@ g.node:hover {
|
||||
overflow: hidden;
|
||||
transition: 0.15s;
|
||||
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
|
||||
display: flex;
|
||||
|
||||
.menu-button {
|
||||
outline: 0 !important;
|
||||
@ -75,11 +76,19 @@ g.node:hover {
|
||||
}
|
||||
|
||||
.extended {
|
||||
width: 296px !important;
|
||||
width: 570px !important;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
mat-divider.divider {
|
||||
height: 40px;
|
||||
margin-left: 1px;
|
||||
margin-right: 7px;
|
||||
width: 10px;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
/** fixes gray background of drawing menu on Firefox **/
|
||||
.mat-drawer-content {
|
||||
|
@ -39,9 +39,14 @@ import { SelectionTool } from '../../cartography/tools/selection-tool';
|
||||
import { RecentlyOpenedProjectService } from '../../services/recentlyOpenedProject.service';
|
||||
import { MapLinkToLinkConverter } from '../../cartography/converters/map/map-link-to-link-converter';
|
||||
import { Link } from '../../models/link';
|
||||
import { Project } from '../../models/project';
|
||||
import { CapturingSettings } from '../../models/capturingSettings';
|
||||
import { LinkWidget } from '../../cartography/widgets/link';
|
||||
|
||||
export class MockedProgressService {
|
||||
public activate() {}
|
||||
|
||||
public deactivate() {}
|
||||
}
|
||||
|
||||
export class MockedNodeService {
|
||||
@ -59,6 +64,22 @@ export class MockedNodeService {
|
||||
delete(server: Server, node: Node) {
|
||||
return of();
|
||||
}
|
||||
|
||||
startAll(server: Server, project: Project) {
|
||||
return of();
|
||||
}
|
||||
|
||||
stopAll(server: Server, project: Project) {
|
||||
return of();
|
||||
}
|
||||
|
||||
suspendAll(server: Server, project: Project) {
|
||||
return of();
|
||||
}
|
||||
|
||||
reloadAll(server: Server, project: Project) {
|
||||
return of();
|
||||
}
|
||||
}
|
||||
|
||||
export class MockedDrawingService {
|
||||
@ -93,8 +114,16 @@ export class MockedDrawingService {
|
||||
export class MockedLinkService {
|
||||
constructor() {}
|
||||
|
||||
getLink(server: Server, projectId: string, linkId: string) {
|
||||
return of({});
|
||||
}
|
||||
|
||||
deleteLink(_server: Server, link: Link){
|
||||
return of({})
|
||||
return of({});
|
||||
}
|
||||
|
||||
updateLink(server: Server, link: Link) {
|
||||
return of({});
|
||||
}
|
||||
|
||||
createLink() {
|
||||
@ -104,11 +133,21 @@ export class MockedLinkService {
|
||||
updateNodes() {
|
||||
return of({});
|
||||
}
|
||||
|
||||
startCaptureOnLink(server: Server, link: Link, settings: CapturingSettings) {
|
||||
return of({});
|
||||
}
|
||||
|
||||
getAvailableFilters(server: Server, link: Link) {
|
||||
return of({});
|
||||
}
|
||||
}
|
||||
|
||||
export class MockedDrawingsDataSource {
|
||||
add() {}
|
||||
|
||||
clear() {}
|
||||
|
||||
get() {
|
||||
return of({});
|
||||
}
|
||||
@ -121,8 +160,10 @@ export class MockedDrawingsDataSource {
|
||||
export class MockedNodesDataSource {
|
||||
add() {}
|
||||
|
||||
clear() {}
|
||||
|
||||
get() {
|
||||
return of({});
|
||||
return {status: 'started'};
|
||||
}
|
||||
|
||||
update() {
|
||||
@ -130,11 +171,17 @@ export class MockedNodesDataSource {
|
||||
}
|
||||
}
|
||||
|
||||
export class MockedLinksDataSource {
|
||||
clear() {}
|
||||
}
|
||||
|
||||
describe('ProjectMapComponent', () => {
|
||||
let component: ProjectMapComponent;
|
||||
let fixture: ComponentFixture<ProjectMapComponent>;
|
||||
let drawingService = new MockedDrawingService();
|
||||
let drawingsDataSource = new MockedDrawingsDataSource();
|
||||
let nodesDataSource = new MockedNodesDataSource();
|
||||
let linksDataSource = new MockedLinksDataSource();
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
@ -150,13 +197,14 @@ describe('ProjectMapComponent', () => {
|
||||
{ provide: ProjectWebServiceHandler },
|
||||
{ provide: MapChangeDetectorRef },
|
||||
{ provide: NodeWidget },
|
||||
{ provide: LinkWidget },
|
||||
{ provide: DrawingsWidget },
|
||||
{ provide: MapNodeToNodeConverter },
|
||||
{ provide: MapDrawingToDrawingConverter },
|
||||
{ provide: MapLabelToLabelConverter },
|
||||
{ provide: MapLinkToLinkConverter },
|
||||
{ provide: NodesDataSource },
|
||||
{ provide: LinksDataSource },
|
||||
{ provide: NodesDataSource, useValue: nodesDataSource },
|
||||
{ provide: LinksDataSource, useValue: linksDataSource },
|
||||
{ provide: DrawingsDataSource, useValue: drawingsDataSource },
|
||||
{ provide: SettingsService, useClass: MockedSettingsService },
|
||||
{ provide: ToolsService },
|
||||
@ -177,6 +225,10 @@ describe('ProjectMapComponent', () => {
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component.ngOnDestroy();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
@ -30,7 +30,7 @@ import { MapNodeToNodeConverter } from '../../cartography/converters/map/map-nod
|
||||
import { SettingsService, Settings } from '../../services/settings.service';
|
||||
import { D3MapComponent } from '../../cartography/components/d3-map/d3-map.component';
|
||||
import { ToolsService } from '../../services/tools.service';
|
||||
import { DrawingContextMenu } from '../../cartography/events/event-source';
|
||||
import { DrawingContextMenu, LinkContextMenu } from '../../cartography/events/event-source';
|
||||
import { MapDrawingToDrawingConverter } from '../../cartography/converters/map/map-drawing-to-drawing-converter';
|
||||
import { SelectionManager } from '../../cartography/managers/selection-manager';
|
||||
import { SelectionTool } from '../../cartography/tools/selection-tool';
|
||||
@ -42,6 +42,7 @@ import { MapLabelToLabelConverter } from '../../cartography/converters/map/map-l
|
||||
import { RecentlyOpenedProjectService } from '../../services/recentlyOpenedProject.service';
|
||||
import { MapLink } from '../../cartography/models/map/map-link';
|
||||
import { MapLinkToLinkConverter } from '../../cartography/converters/map/map-link-to-link-converter';
|
||||
import { LinkWidget } from '../../cartography/widgets/link';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -95,6 +96,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
private mapChangeDetectorRef: MapChangeDetectorRef,
|
||||
private nodeWidget: NodeWidget,
|
||||
private drawingsWidget: DrawingsWidget,
|
||||
private linkWidget: LinkWidget,
|
||||
private mapNodeToNode: MapNodeToNodeConverter,
|
||||
private mapDrawingToDrawing: MapDrawingToDrawingConverter,
|
||||
private mapLabelToLabel: MapLabelToLabelConverter,
|
||||
@ -219,6 +221,11 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
this.toolsService.selectionToolActivation(true);
|
||||
}
|
||||
|
||||
const onLinkContextMenu = this.linkWidget.onContextMenu.subscribe((eventLink: LinkContextMenu) => {
|
||||
const link = this.mapLinkToLink.convert(eventLink.link);
|
||||
this.contextMenu.openMenuForListOfElements([], [], [], [link], eventLink.event.pageY, eventLink.event.pageX);
|
||||
});
|
||||
|
||||
const onNodeContextMenu = this.nodeWidget.onContextMenu.subscribe((eventNode: NodeContextMenu) => {
|
||||
const node = this.mapNodeToNode.convert(eventNode.node);
|
||||
this.contextMenu.openMenuForNode(node, eventNode.event.pageY, eventNode.event.pageX);
|
||||
@ -253,6 +260,7 @@ export class ProjectMapComponent implements OnInit, OnDestroy {
|
||||
this.contextMenu.openMenuForListOfElements(drawings, nodes, labels, links, event.pageY, event.pageX);
|
||||
});
|
||||
|
||||
this.subscriptions.push(onLinkContextMenu);
|
||||
this.subscriptions.push(onNodeContextMenu);
|
||||
this.subscriptions.push(onDrawingContextMenu);
|
||||
this.subscriptions.push(onContextMenu);
|
||||
|
@ -21,11 +21,11 @@
|
||||
>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- <div>
|
||||
<mat-checkbox [(ngModel)]="settings.angular_map"
|
||||
>Enable experimental Angular Map (WARNING: IT CAN BREAK YOU LABS!)</mat-checkbox
|
||||
>
|
||||
</div>
|
||||
</div> -->
|
||||
</mat-expansion-panel>
|
||||
|
||||
<mat-expansion-panel [expanded]="true">
|
||||
|
@ -3,9 +3,14 @@
|
||||
<button mat-button class="top-button" color="accent" (click)="onNoClick()" routerLink="/server/{{server.id}}/project/{{project.project_id}}/snapshots">Go to snapshots</button>
|
||||
</div>
|
||||
<div mat-dialog-content>
|
||||
<mat-form-field class="name-input">
|
||||
<input matInput tabindex="1" [(ngModel)]="snapshot.name" placeholder="Name" />
|
||||
</mat-form-field>
|
||||
<form [formGroup]="inputForm">
|
||||
<mat-form-field class="name-input">
|
||||
<input
|
||||
matInput tabindex="1"
|
||||
formControlName="snapshotName"
|
||||
placeholder="Name" />
|
||||
</mat-form-field>
|
||||
</form>
|
||||
</div>
|
||||
<div mat-dialog-actions>
|
||||
<button mat-button (click)="onNoClick()" tabindex="-1" color="accent">No Thanks</button>
|
||||
|
@ -3,6 +3,11 @@ import { Snapshot } from '../../../models/snapshot';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material';
|
||||
import { Server } from '../../../models/server';
|
||||
import { Project } from '../../../models/project';
|
||||
import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms';
|
||||
import { ToasterService } from '../../../services/toaster.service';
|
||||
import { SnapshotService } from '../../../services/snapshot.service';
|
||||
import { NodesDataSource } from '../../../cartography/datasources/nodes-datasource';
|
||||
import { Node } from '../../../cartography/models/node';
|
||||
|
||||
|
||||
@Component({
|
||||
@ -14,17 +19,53 @@ export class CreateSnapshotDialogComponent {
|
||||
server: Server;
|
||||
project: Project;
|
||||
snapshot: Snapshot = new Snapshot();
|
||||
inputForm: FormGroup;
|
||||
snapshots: string[] = [];
|
||||
isInRunningState: boolean;
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<CreateSnapshotDialogComponent>,
|
||||
private formBuilder: FormBuilder,
|
||||
private toasterService: ToasterService,
|
||||
private snapshotService: SnapshotService,
|
||||
private nodesDataSource: NodesDataSource,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any
|
||||
) {
|
||||
this.server = data['server'];
|
||||
this.project = data['project'];
|
||||
|
||||
this.inputForm = this.formBuilder.group({
|
||||
snapshotName: new FormControl('', Validators.required)
|
||||
});
|
||||
|
||||
this.snapshotService.list(this.server, this.project.project_id).subscribe((snapshots: Snapshot[]) => {
|
||||
snapshots.forEach((snapshot: Snapshot) => {
|
||||
this.snapshots.push(snapshot.name);
|
||||
});
|
||||
});
|
||||
|
||||
this.nodesDataSource.getItems().forEach((node: Node) => {
|
||||
if (node.status !== 'stopped' && !this.isAlwaysRunningNode(node.node_type)) {
|
||||
this.isInRunningState = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isAlwaysRunningNode(nodeType: string) {
|
||||
return !["qemu", "docker", "dynamips", "vpcs", "vmware", "virtualbox", "iou", "traceng"].includes(nodeType);
|
||||
}
|
||||
|
||||
onAddClick(): void {
|
||||
this.dialogRef.close(this.snapshot);
|
||||
if (this.inputForm.invalid) {
|
||||
this.toasterService.error(`Fill all required fields`);
|
||||
} else if (this.snapshots.includes(this.inputForm.get('snapshotName').value)) {
|
||||
this.toasterService.error(`Snapshot with this name already exists`);
|
||||
} else if (this.isInRunningState) {
|
||||
this.toasterService.error(`Project must be stopped in order to export it`);
|
||||
} else {
|
||||
this.snapshot.name = this.inputForm.get('snapshotName').value;
|
||||
this.dialogRef.close(this.snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
onNoClick(): void {
|
||||
|
@ -1 +1 @@
|
||||
<button mat-icon-button (click)="createSnapshotModal()"><mat-icon>snooze</mat-icon></button>
|
||||
<button matTooltip="Manage snapshots" mat-icon-button (click)="createSnapshotModal()"><mat-icon>snooze</mat-icon></button>
|
||||
|
@ -48,11 +48,6 @@ export class SnapshotMenuItemComponent implements OnInit {
|
||||
(created_snapshot: Snapshot) => {
|
||||
this.toaster.success(`Snapshot '${snapshot.name}' has been created.`);
|
||||
progress.close();
|
||||
},
|
||||
response => {
|
||||
const error = response.json();
|
||||
this.toaster.error(`Cannot create snapshot: ${error.message}`);
|
||||
progress.close();
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -1 +1 @@
|
||||
<button mat-icon-button (click)="listTemplatesModal()"><mat-icon>add_to_queue</mat-icon></button>
|
||||
<button matTooltip="Browse all devices" mat-icon-button (click)="listTemplatesModal()"><mat-icon>add_to_queue</mat-icon></button>
|
||||
|
@ -67,6 +67,10 @@ header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0%;
|
||||
}
|
||||
|
||||
.mat-dialog-content > * {
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -32,6 +32,6 @@
|
||||
<app-progress></app-progress>
|
||||
|
||||
<footer class="footer mat-app-background">
|
||||
GNS3 Web UI © 2019
|
||||
GNS3 Web UI © 2019 - v{{ uiVersion }}
|
||||
</footer>
|
||||
|
||||
|
@ -17,17 +17,20 @@ class ElectronServiceMock {
|
||||
public isElectronApp: boolean;
|
||||
}
|
||||
|
||||
class MockedServerManagementService {
|
||||
public serverStatusChanged;
|
||||
public stopAll() {}
|
||||
}
|
||||
|
||||
describe('DefaultLayoutComponent', () => {
|
||||
let component: DefaultLayoutComponent;
|
||||
let fixture: ComponentFixture<DefaultLayoutComponent>;
|
||||
let electronServiceMock: ElectronServiceMock;
|
||||
let serverManagementService;
|
||||
let serverManagementService = new MockedServerManagementService();
|
||||
|
||||
beforeEach(async(() => {
|
||||
electronServiceMock = new ElectronServiceMock();
|
||||
serverManagementService = {
|
||||
serverStatusChanged: new Subject<ServerStateEvent>()
|
||||
};
|
||||
serverManagementService.serverStatusChanged = new Subject<ServerStateEvent>();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [DefaultLayoutComponent, ProgressComponent],
|
||||
|
@ -5,6 +5,8 @@ import { ServerManagementService } from '../../services/server-management.servic
|
||||
import { Subscription } from 'rxjs';
|
||||
import { ToasterService } from '../../services/toaster.service';
|
||||
import { ProgressService } from '../../common/progress/progress.service';
|
||||
import { version } from './../../version';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-default-layout',
|
||||
@ -14,7 +16,8 @@ import { ProgressService } from '../../common/progress/progress.service';
|
||||
})
|
||||
export class DefaultLayoutComponent implements OnInit, OnDestroy {
|
||||
public isInstalledSoftwareAvailable = false;
|
||||
|
||||
public uiVersion = version;
|
||||
|
||||
serverStatusSubscription: Subscription;
|
||||
shouldStopServersOnClosing = true;
|
||||
|
||||
|
@ -19,7 +19,8 @@ import {
|
||||
MatTooltipModule,
|
||||
MatStepperModule,
|
||||
MatRadioModule,
|
||||
MatGridListModule
|
||||
MatGridListModule,
|
||||
MatTabsModule
|
||||
} from '@angular/material';
|
||||
|
||||
export const MATERIAL_IMPORTS = [
|
||||
@ -43,5 +44,6 @@ export const MATERIAL_IMPORTS = [
|
||||
MatTooltipModule,
|
||||
MatStepperModule,
|
||||
MatRadioModule,
|
||||
MatGridListModule
|
||||
MatGridListModule,
|
||||
MatTabsModule
|
||||
];
|
||||
|
4
src/app/models/capturingSettings.ts
Normal file
4
src/app/models/capturingSettings.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export class CapturingSettings {
|
||||
capture_file_name: string;
|
||||
data_link_type: string;
|
||||
}
|
14
src/app/models/filter-description.ts
Normal file
14
src/app/models/filter-description.ts
Normal file
@ -0,0 +1,14 @@
|
||||
export class FilterDescription {
|
||||
description: string;
|
||||
name: string;
|
||||
parameters: Parameter[];
|
||||
type: string;
|
||||
}
|
||||
|
||||
interface Parameter {
|
||||
maximum?: number;
|
||||
minimum?: number;
|
||||
name: string;
|
||||
type: string;
|
||||
unit?: string;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user