Added native Bazel build to GUI2. Reduced a lot of the unused Angular CLI structures

Reviewers should look at the changes in WORKSPACE, BUILD, BUILD.bazel, README.md files
This is only possible now as rules_nodejs went to 1.0.0 on December 20
gui2 has now been made the entry point (rather than gui2-fw-lib)
No tests or linting are functional yet for Typescript
Each NgModule now has its own BUILD.bazel file with ng_module
gui2-fw-lib is all one module and has been refactored to simplify the directory structure
gui2-topo-lib is also all one module - its directory structure has had 3 layers removed
The big bash script in web/gui2/BUILD has been removed - all is done through ng_module rules
in web/gui2/src/main/webapp/BUILD.bazel and web/gui2/src/main/webapp/app/BUILD.bazel

Change-Id: Ifcfcc23a87be39fe6d6c8324046cc8ebadb90551
diff --git a/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.spec.ts b/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.spec.ts
new file mode 100644
index 0000000..6ee354a
--- /dev/null
+++ b/web/gui2-topo-lib/lib/layer/forcesvg/forcesvg.component.spec.ts
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the 'License');
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an 'AS IS' BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import {async, ComponentFixture, TestBed} from '@angular/core/testing';
+
+import {ForceSvgComponent} from './forcesvg.component';
+import {
+    FnService, IconService,
+    LionService,
+    LogService, SvgUtilService,
+    UrlFnService,
+    WebSocketService
+} from '../../../gui2-fw-lib/public_api';
+import {DraggableDirective} from './draggable/draggable.directive';
+import {ActivatedRoute, Params} from '@angular/router';
+import {of} from 'rxjs';
+import {DeviceNodeSvgComponent} from './visuals/devicenodesvg/devicenodesvg.component';
+import {SubRegionNodeSvgComponent} from './visuals/subregionnodesvg/subregionnodesvg.component';
+import {HostNodeSvgComponent} from './visuals/hostnodesvg/hostnodesvg.component';
+import {LinkSvgComponent} from './visuals/linksvg/linksvg.component';
+import {Device, Host, Link, LinkType, Region} from './models';
+import {ChangeDetectorRef, SimpleChange} from '@angular/core';
+import {TopologyService} from '../../topology.service';
+import {BadgeSvgComponent} from './visuals/badgesvg/badgesvg.component';
+
+class MockActivatedRoute extends ActivatedRoute {
+    constructor(params: Params) {
+        super();
+        this.queryParams = of(params);
+    }
+}
+
+class MockIconService {
+    loadIconDef() { }
+}
+
+class MockSvgUtilService {
+
+    cat7() {
+        const tcid = 'd3utilTestCard';
+
+        function getColor(id, muted, theme) {
+            // NOTE: since we are lazily assigning domain ids, we need to
+            //       get the color from all 4 scales, to keep the domains
+            //       in sync.
+            const ln = '#5b99d2';
+            const lm = '#9ebedf';
+            const dn = '#5b99d2';
+            const dm = '#9ebedf';
+            if (theme === 'dark') {
+                return muted ? dm : dn;
+            } else {
+                return muted ? lm : ln;
+            }
+        }
+
+        return {
+            // testCard: testCard,
+            getColor: getColor,
+        };
+    }
+}
+
+class MockUrlFnService { }
+
+class MockWebSocketService {
+    createWebSocket() { }
+    isConnected() { return false; }
+    unbindHandlers() { }
+    bindHandlers() { }
+}
+
+class MockTopologyService {
+    public instancesIndex: Map<string, number>;
+    constructor() {
+        this.instancesIndex = new Map();
+    }
+}
+
+describe('ForceSvgComponent', () => {
+    let fs: FnService;
+    let ar: MockActivatedRoute;
+    let windowMock: Window;
+    let logServiceSpy: jasmine.SpyObj<LogService>;
+    let component: ForceSvgComponent;
+    let fixture: ComponentFixture<ForceSvgComponent>;
+    const openflowSampleData = require('./tests/test-module-topo2CurrentRegion.json');
+    const openflowRegionData: Region = <Region><unknown>(openflowSampleData.payload);
+
+    const odtnSampleData = require('./tests/test-OdtnConfig-topo2CurrentRegion.json');
+    const odtnRegionData: Region = <Region><unknown>(odtnSampleData.payload);
+
+    const emptyRegion: Region = <Region>{devices: [ [], [], [] ], hosts: [ [], [], [] ], links: []};
+
+    beforeEach(() => {
+        const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
+        ar = new MockActivatedRoute({ 'debug': 'txrx' });
+
+        windowMock = <any>{
+            location: <any>{
+                hostname: 'foo',
+                host: 'foo',
+                port: '80',
+                protocol: 'http',
+                search: { debug: 'true' },
+                href: 'ws://foo:123/onos/ui/websock/path',
+                absUrl: 'ws://foo:123/onos/ui/websock/path'
+            }
+        };
+
+        const bundleObj = {
+            'core.view.Topo': {
+                test: 'test1'
+            }
+        };
+        const mockLion = (key) => {
+            return bundleObj[key] || '%' + key + '%';
+        };
+
+        fs = new FnService(ar, logSpy, windowMock);
+
+        TestBed.configureTestingModule({
+            declarations: [
+                ForceSvgComponent,
+                DeviceNodeSvgComponent,
+                HostNodeSvgComponent,
+                SubRegionNodeSvgComponent,
+                LinkSvgComponent,
+                DraggableDirective,
+                BadgeSvgComponent
+            ],
+            providers: [
+                { provide: LogService, useValue: logSpy },
+                { provide: ActivatedRoute, useValue: ar },
+                { provide: FnService, useValue: fs },
+                { provide: ChangeDetectorRef, useClass: ChangeDetectorRef },
+                { provide: UrlFnService, useClass: MockUrlFnService },
+                { provide: WebSocketService, useClass: MockWebSocketService },
+                { provide: LionService, useFactory: (() => {
+                        return {
+                            bundle: ((bundleId) => mockLion),
+                            ubercache: new Array(),
+                            loadCbs: new Map<string, () => void>([])
+                        };
+                    })
+                },
+                { provide: IconService, useClass: MockIconService },
+                { provide: SvgUtilService, useClass: MockSvgUtilService },
+                { provide: TopologyService, useClass: MockTopologyService },
+                { provide: 'Window', useValue: windowMock },
+            ]
+        })
+        .compileComponents();
+        logServiceSpy = TestBed.get(LogService);
+
+        fixture = TestBed.createComponent(ForceSvgComponent);
+        component = fixture.debugElement.componentInstance;
+        fixture.detectChanges();
+    });
+
+    it('should create', () => {
+        expect(component).toBeTruthy();
+    });
+
+    it('load sample files', () => {
+        expect(openflowSampleData).toBeTruthy();
+        expect(openflowSampleData.payload).toBeTruthy();
+        expect(openflowSampleData.payload.id).toBe('(root)');
+
+        expect(odtnSampleData).toBeTruthy();
+        expect(odtnSampleData.payload).toBeTruthy();
+        expect(odtnSampleData.payload.id).toBe('(root)');
+    });
+
+    it('should read sample data payload as Region', () => {
+        expect(openflowRegionData).toBeTruthy();
+        // console.log(regionData);
+        expect(openflowRegionData.id).toBe('(root)');
+        expect(openflowRegionData.devices).toBeTruthy();
+        expect(openflowRegionData.devices.length).toBe(3);
+        expect(openflowRegionData.devices[2].length).toBe(10);
+        expect(openflowRegionData.hosts.length).toBe(3);
+        expect(openflowRegionData.hosts[2].length).toBe(20);
+        expect(openflowRegionData.links.length).toBe(44);
+    });
+
+    it('should read device246 correctly', () => {
+        const device246: Device = openflowRegionData.devices[2][0];
+        expect(device246.id).toBe('of:0000000000000246');
+        expect(device246.nodeType).toBe('device');
+        expect(device246.type).toBe('switch');
+        expect(device246.online).toBe(true);
+        expect(device246.master).toBe('10.192.19.68');
+        expect(device246.layer).toBe('def');
+
+        expect(device246.props.managementAddress).toBe('10.192.19.69');
+        expect(device246.props.protocol).toBe('OF_13');
+        expect(device246.props.driver).toBe('ofdpa-ovs');
+        expect(device246.props.latitude).toBe('40.15');
+        expect(device246.props.name).toBe('s246');
+        expect(device246.props.locType).toBe('geo');
+        expect(device246.props.channelId).toBe('10.192.19.69:59980');
+        expect(device246.props.longitude).toBe('-121.679');
+
+        expect(device246.location.locType).toBe('geo');
+        expect(device246.location.latOrY).toBe(40.15);
+        expect(device246.location.longOrX).toBe(-121.679);
+    });
+
+    it('should read host 3 correctly', () => {
+        const host3: Host = openflowRegionData.hosts[2][0];
+        expect(host3.id).toBe('00:88:00:00:00:03/110');
+        expect(host3.nodeType).toBe('host');
+        expect(host3.layer).toBe('def');
+        expect(host3.configured).toBe(false);
+        expect(host3.ips.length).toBe(3);
+        expect(host3.ips[0]).toBe('fe80::288:ff:fe00:3');
+        expect(host3.ips[1]).toBe('2000::102');
+        expect(host3.ips[2]).toBe('10.0.1.2');
+    });
+
+    it('should read link 3-205 correctly', () => {
+        const link3_205: Link = openflowRegionData.links[0];
+        expect(link3_205.id).toBe('00:AA:00:00:00:03/None~of:0000000000000205/6');
+        expect(link3_205.epA).toBe('00:AA:00:00:00:03/None');
+        expect(link3_205.epB).toBe('of:0000000000000205');
+        expect(String(LinkType[link3_205.type])).toBe('2');
+        expect(link3_205.portA).toBe(undefined);
+        expect(link3_205.portB).toBe('6');
+
+        expect(link3_205.rollup).toBeTruthy();
+        expect(link3_205.rollup.length).toBe(1);
+        expect(link3_205.rollup[0].id).toBe('00:AA:00:00:00:03/None~of:0000000000000205/6');
+        expect(link3_205.rollup[0].epA).toBe('00:AA:00:00:00:03/None');
+        expect(link3_205.rollup[0].epB).toBe('of:0000000000000205');
+        expect(String(LinkType[link3_205.rollup[0].type])).toBe('2');
+        expect(link3_205.rollup[0].portA).toBe(undefined);
+        expect(link3_205.rollup[0].portB).toBe('6');
+
+    });
+
+    it('should handle regionData change - empty Region', () => {
+        component.ngOnChanges(
+            {'regionData' : new SimpleChange(<Region>{}, emptyRegion, true)});
+
+        expect(component.graph.nodes.length).toBe(0);
+    });
+
+    it('should know how to format names', () => {
+        expect(ForceSvgComponent.extractNodeName('00:AA:00:00:00:03/None', undefined))
+            .toEqual('00:AA:00:00:00:03/None');
+
+        expect(ForceSvgComponent.extractNodeName('00:AA:00:00:00:03/161', '161'))
+            .toEqual('00:AA:00:00:00:03');
+
+        // Like epB of first example in sampleData file - endPtStr contains port number
+        expect(ForceSvgComponent.extractNodeName('of:0000000000000206/6', '6'))
+            .toEqual('of:0000000000000206');
+
+        // Like epB of second example in sampleData file - endPtStr does not contain port number
+        expect(ForceSvgComponent.extractNodeName('of:0000000000000206', '6'))
+            .toEqual('of:0000000000000206');
+    });
+
+    it('should handle openflow regionData change - sample Region', () => {
+        component.regionData = openflowRegionData;
+        component.ngOnChanges(
+            {'regionData' : new SimpleChange(<Region>{}, openflowRegionData, true)});
+
+        expect(component.graph.nodes.length).toBe(30);
+
+        expect(component.graph.links.length).toBe(44);
+
+    });
+
+    it('should handle odtn regionData change - sample odtn Region', () => {
+        component.regionData = odtnRegionData;
+        component.ngOnChanges(
+            {'regionData' : new SimpleChange(<Region>{}, odtnRegionData, true)});
+
+        expect(component.graph.nodes.length).toBe(2);
+
+        expect(component.graph.links.length).toBe(6);
+
+    });
+});