Rift.IO OSM R1 Initial Submission

Signed-off-by: Jeremy Mordkoff <jeremy.mordkoff@riftio.com>
diff --git a/skyquake/tests/intern.js b/skyquake/tests/intern.js
new file mode 100644
index 0000000..9b4fd44
--- /dev/null
+++ b/skyquake/tests/intern.js
@@ -0,0 +1,54 @@
+// Learn more about configuring this file at <https://theintern.github.io/intern/#configuration>.
+// These default settings work OK for most people. The options that *must* be changed below are the
+// packages, suites, excludeInstrumentation, and (if you want functional tests) functionalSuites
+define({
+	// Default desired capabilities for all environments. Individual capabilities can be overridden by any of the
+	// specified browser environments in the `environments` array below as well. See
+	// <https://theintern.github.io/intern/#option-capabilities> for links to the different capabilities options for
+	// different services.
+	//
+	// Note that the `build` capability will be filled in with the current commit ID or build tag from the CI
+	// environment automatically
+	capabilities: {
+		'browserstack.selenium_version': '2.45.0'
+	},
+
+	// Browsers to run integration testing against. Note that version numbers must be strings if used with Sauce
+	// OnDemand. Options that will be permutated are browserName, version, platform, and platformVersion; any other
+	// capabilities options specified for an environment will be copied as-is
+	environments: [
+		{ browserName: 'internet explorer', version: '11', platform: 'WIN8' },
+		{ browserName: 'internet explorer', version: '10', platform: 'WIN8' },
+		{ browserName: 'internet explorer', version: '9', platform: 'WINDOWS' },
+		{ browserName: 'firefox', version: '37', platform: [ 'WINDOWS', 'MAC' ] },
+		{ browserName: 'chrome', version: '39', platform: [ 'WINDOWS', 'MAC' ] },
+		{ browserName: 'safari', version: '8', platform: 'MAC' }
+	],
+
+	// Maximum number of simultaneous integration tests that should be executed on the remote WebDriver service
+	maxConcurrency: 2,
+
+	// Name of the tunnel class to use for WebDriver tests.
+	// See <https://theintern.github.io/intern/#option-tunnel> for built-in options
+	tunnel: 'BrowserStackTunnel',
+
+	// Configuration options for the module loader; any AMD configuration options supported by the AMD loader in use
+	// can be used here.
+	// If you want to use a different loader than the default loader, see
+	// <https://theintern.github.io/intern/#option-useLoader> for instruction
+	loaderOptions: {
+		// Packages that should be registered with the loader in each testing environment
+		packages: [ { name: 'myPackage', location: '.' } ]
+	},
+
+	// Non-functional test suite(s) to run in each browser
+	//suites: [ /* 'myPackage/tests/foo', 'myPackage/tests/bar' */ ],
+    suites: [
+        'tests/unit/plugin_discoverer'
+    ],
+	// Functional test suite(s) to execute against each browser once non-functional tests are completed
+	functionalSuites: [ /* 'myPackage/tests/functional' */ ],
+
+	// A regular expression matching URLs to files that should not be included in code coverage analysis
+	excludeInstrumentation: /^(?:tests|node_modules)\//
+});
diff --git a/skyquake/tests/react/skyquakeRouter_test.js b/skyquake/tests/react/skyquakeRouter_test.js
new file mode 100644
index 0000000..0c65bbd
--- /dev/null
+++ b/skyquake/tests/react/skyquakeRouter_test.js
@@ -0,0 +1,34 @@
+jest.dontMock('../framework/widgets/skyquake_container/skyquakeNav');
+
+import React from 'react';
+import ReactDOM from 'react-dom';
+import TestUtils from 'react-addons-test-utils';
+import SkyquakeNav from '../framework/widgets/skyquake_container/skyquakeNav';
+// const SkyquakeNav = require('../framework/widgets/skyquake_container/skyquakeNav');
+
+describe('SkyquakeNav', () => {
+
+    let exampleRoutes = [{
+        "label": "Hello World Component 1",
+        "route": "/helloworld/#hello",
+        "component": "./helloWorldOne.jsx",
+        "path": "",
+        "type": "internal",
+        "isExternal": true
+    }];
+    let node;
+    beforeEach(function() {
+        node = document.createElement('div')
+    });
+    describe('returnLinkItem', () => {
+        let element;
+        beforeEach(function() {
+            element = SkyquakeNav.returnLinkItem(exampleRoutes[0]);
+        });
+        it('Returns an <a> tag when external', () => {
+            ReactDOM.render(element, node, function() {
+                expect(node.tagName == "A";
+            })
+        })
+    })
+})
diff --git a/skyquake/tests/stories/button.js b/skyquake/tests/stories/button.js
new file mode 100644
index 0000000..269312d
--- /dev/null
+++ b/skyquake/tests/stories/button.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { storiesOf, action } from '@kadira/storybook';
+import Button from '../../framework/widgets/button/rw.button'
+import SqButton from '../../framework/widgets/button/sq-button'
+import reactToJsx from 'react-to-jsx';
+import StyleGuideItem from 'react-style-guide';
+import '../../node_modules/react-style-guide/node_modules/highlight.js/styles/github.css';
+
+let sqButtonHTML = ( <div style={{display: 'flex', padding: '10px'}}>
+        <SqButton  icon="check" label="Normal" className="dark"/>
+        <SqButton  label="Medium No Icon" size="medium" className="dark"/>
+        <SqButton size="large"  icon="check" label="Large" className="dark"/>
+        <SqButton size="large" primary  icon="check" label="Large" className="dark"/>
+        <SqButton size="large" disabled icon="check" label="Disabled" className="dark"/>
+    </div>)
+
+storiesOf('Button', module)
+  .add('A Light Button', () => (
+    <Button onClick={action('clicked')} label="A Light Button!" className="light" />
+  ))
+  .add('A Dark Button', () => (
+    <StyleGuideItem>
+    <Button  label="A Dark Button!" className="dark"/>
+    </StyleGuideItem>
+  ))
+  .add('A Sq Button', () => (
+                             <StyleGuideItem>
+   <div style={{display: 'flex', margin: '10px 10px 50px 10px'}}>
+        <SqButton  icon="check" label="Submit" />
+        <SqButton  label="Medium No Icon" size="medium" />
+        <SqButton size="large"  icon="check" label="Large" />
+        <SqButton size="large" primary  icon="check" label="Large" />
+        <SqButton size="large" disabled icon="check" label="Disabled" />
+        </div>
+    </StyleGuideItem>
+  ));
diff --git a/skyquake/tests/stories/catalogCard.js b/skyquake/tests/stories/catalogCard.js
new file mode 100644
index 0000000..b7e0e57
--- /dev/null
+++ b/skyquake/tests/stories/catalogCard.js
@@ -0,0 +1,538 @@
+import React from 'react';
+import { storiesOf, action } from '@kadira/storybook';
+import CatalogCard from '../../plugins/launchpad/src/instantiate/catalogCard.jsx'
+import CatalogDescriptorRaw from '../../plugins/launchpad/src/instantiate/catalogDescriptorRaw.jsx'
+import InstantiateDescriptorPanel from '../../plugins/launchpad/src/instantiate/instantiateDescriptorPanel.jsx'
+import InstantiateSelectDescriptorPanel from '../../plugins/launchpad/src/instantiate/InstantiateSelectDescriptorPanel.jsx'
+import InstantiateInputParams from '../../plugins/launchpad/src/instantiate/instantiateInputParams.jsx'
+import reactToJsx from 'react-to-jsx';
+import InstantiateStore from '../../plugins/launchpad/src/instantiate/instantiateStore.js';
+import Alt from '../../framework/widgets/skyquake_container/skyquakeAltInstance.js'
+import {Panel, PanelWrapper} from '../../framework/widgets/panel/panel.jsx'
+import '../../node_modules/open-iconic/font/css/open-iconic.css';
+import 'style/base.scss';
+const Store = Alt.createStore(InstantiateStore)
+// import StyleGuideItem from 'react-style-guide';
+// import '../../node_modules/react-style-guide/node_modules/highlight.js/styles/github.css';
+let SampleNSD = {
+  'name': 'A Sample NSD for Corp A',
+  'short-name': 'A Sample NSD',
+  description: 'A description of the sample NSD',
+  vendor: 'RIFT.io',
+  version: '1.0',
+  "constituent-vnfd": [
+      {
+          "start-by-default": "true",
+          "member-vnf-index": 1,
+          "vnfd-id-ref": "358fe806-57f8-11e6-b7de-6cb3113b406f",
+          "name": "trafgen",
+          "vnf-name": "trafgen"
+      },
+      {
+          "start-by-default": "true",
+          "member-vnf-index": 2,
+          "vnfd-id-ref": "3bd17356-57f8-11e6-88db-6cb3113b406f",
+          "name": "trafsink",
+          "vnf-name": "trafsink"
+      }
+  ],
+  "vld": [
+      {
+          "provider-network": {
+              "physical-network": "physnet1",
+              "overlay-type": "VLAN"
+          },
+          "version": "1.0",
+          "vendor": "RIFT.io",
+          "name": "Link1",
+          "short-name": "Link1",
+          "vnfd-connection-point-ref": [
+              {
+                  "vnfd-connection-point-ref": "trafgen/cp0",
+                  "vnfd-id-ref": "358fe806-57f8-11e6-b7de-6cb3113b406f",
+                  "member-vnf-index-ref": 1
+              },
+              {
+                  "vnfd-connection-point-ref": "trafsink/cp0",
+                  "vnfd-id-ref": "3bd17356-57f8-11e6-88db-6cb3113b406f",
+                  "member-vnf-index-ref": 2
+              }
+          ],
+          "description": "Link",
+          "id": "4ef5eebc-57f8-11e6-87d1-6cb3113b406f",
+          "type": "ELAN"
+      }
+  ],
+  "logo": "riftio.png",
+  "vnffgd": [
+        {
+            "name": "vnffgd-1",
+            "id": "23ee7",
+            "short-name": "FG-1"
+        }
+    ]
+}
+
+storiesOf('Instantiate Components', module)
+.add('CatalogCard', () => {
+  let cards = [];
+  for(let i = 0; i < 10; i++) {
+    cards.push(<CatalogCard key={i} clickid={i} descriptor={SampleNSD} />)
+  }
+  return (
+<PanelWrapper>
+<Panel>
+  <div style={{display:'flex', flexWrap:'wrap'}}>
+
+
+  <CatalogCard descriptor={SampleNSD} isSelected={true} isActive={true} />
+  {
+    cards
+  }
+  </div>
+</Panel>
+</PanelWrapper>
+)}
+)
+
+.add('InstantiateDescriptorPanel', () => (
+  <PanelWrapper>
+    <InstantiateDescriptorPanel
+      descriptor={returnNSD()[0]}
+    />
+    <Panel title="Input Parameters">
+    </Panel>
+  </PanelWrapper>
+))
+.add('InstantiateSelectDescriptor', () => (
+  <PanelWrapper>
+    <InstantiateSelectDescriptorPanel
+      descriptors={returnNSD()}
+    />
+  </PanelWrapper>
+))
+.add('Descriptor Review', () => (
+ <PanelWrapper>
+  <Panel title="Select Descriptor">
+    <CatalogCard descriptor={SampleNSD} isSelected={true} isActive={true} />
+  </Panel>
+  <Panel title="Descriptor Preview">
+    <CatalogDescriptorRaw descriptor={SampleNSD} />
+  </Panel>
+</PanelWrapper>))
+
+function returnNSD() {
+  return [
+            {
+                "input-parameter-xpath": [
+                    {
+                        "xpath": "/nsd:nsd-catalog/nsd:nsd/nsd:vendor"
+                    }
+                ],
+                "ip-profiles": [
+                    {
+                        "name": "InterVNFLink",
+                        "description": "Inter VNF Link",
+                        "ip-profile-params": {
+                            "ip-version": "ipv4",
+                            "gateway-address": "31.31.31.210",
+                            "subnet-address": "31.31.31.0/24",
+                            "dhcp-params": {
+                                "enabled": "true"
+                            }
+                        }
+                    }
+                ],
+                "version": "1.0",
+                "initial-config-primitive": [
+                    {
+                        "seq": 1,
+                        "name": "start traffic",
+                        "parameter": [
+                            {
+                                "name": "userid"
+                            }
+                        ],
+                        "user-defined-script": "start_traffic.py"
+                    }
+                ],
+                "name": "ping_pong_nsd",
+                "short-name": "ping_pong_nsd",
+                "id": "1b85e414-630c-11e6-9050-3eca272c7412",
+                "logo": "rift_logo.png",
+                "description": "Toy NS",
+                "constituent-vnfd": [
+                    {
+                        "member-vnf-index": 1,
+                        "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                        "start-by-default": "true",
+                        "name": "ping_vnfd",
+                        "vnf-name": "ping_vnfd"
+                    },
+                    {
+                        "member-vnf-index": 2,
+                        "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                        "start-by-default": "true",
+                        "name": "pong_vnfd",
+                        "vnf-name": "pong_vnfd"
+                    }
+                ],
+                "vendor": "RIFT.io",
+                "placement-groups": [
+                    {
+                        "member-vnfd": [
+                            {
+                                "member-vnf-index-ref": 1,
+                                "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                                "name": "ping_vnfd"
+                            },
+                            {
+                                "member-vnf-index-ref": 2,
+                                "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                                "name": "pong_vnfd"
+                            }
+                        ],
+                        "name": "Orcus",
+                        "requirement": "Place this VM on the Kuiper belt object Orcus",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": []
+                    },
+                    {
+                        "member-vnfd": [
+                            {
+                                "member-vnf-index-ref": 1,
+                                "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                                "name": "ping_vnfd"
+                            },
+                            {
+                                "member-vnf-index-ref": 2,
+                                "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                                "name": "pong_vnfd"
+                            }
+                        ],
+                        "name": "Quaoar",
+                        "requirement": "Place this VM on the Kuiper belt object Quaoar",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": []
+                    }
+                ],
+                "vld": [
+                    {
+                        "type": "ELAN",
+                        "description": "Toy VL",
+                        "version": "1.0",
+                        "ip-profile-ref": "InterVNFLink",
+                        "vendor": "RIFT.io",
+                        "name": "ping_pong_vld",
+                        "short-name": "ping_pong_vld",
+                        "id": "ping_pong_vld1",
+                        "vnfd-connection-point-ref": [
+                            {
+                                "member-vnf-index-ref": 1,
+                                "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                                "vnfd-connection-point-ref": "ping_vnfd/cp0"
+                            },
+                            {
+                                "member-vnf-index-ref": 2,
+                                "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                                "vnfd-connection-point-ref": "pong_vnfd/cp0"
+                            }
+                        ]
+                    }
+                ],
+                "meta": {
+                    "instance-ref-count": 1
+                },
+                "ns-placement-groups": [
+                    {
+                        "member-vnfd": [
+                            {
+                                "member-vnf-index-ref": 1,
+                                "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                                "name": "ping_vnfd"
+                            },
+                            {
+                                "member-vnf-index-ref": 2,
+                                "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                                "name": "pong_vnfd"
+                            }
+                        ],
+                        "name": "Orcus",
+                        "requirement": "Place this VM on the Kuiper belt object Orcus",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": []
+                    },
+                    {
+                        "member-vnfd": [
+                            {
+                                "member-vnf-index-ref": 1,
+                                "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                                "name": "ping_vnfd"
+                            },
+                            {
+                                "member-vnf-index-ref": 2,
+                                "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                                "name": "pong_vnfd"
+                            }
+                        ],
+                        "name": "Quaoar",
+                        "requirement": "Place this VM on the Kuiper belt object Quaoar",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": []
+                    }
+                ],
+                "vnf-placement-groups": [
+                    {
+                        "name": "Eris",
+                        "member-vdus": [
+                            {
+                                "member-vdu-ref": "iovdu_0"
+                            }
+                        ],
+                        "requirement": "Place this VM on the Kuiper belt object Eris",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": [],
+                        "vnf-name": "ping_vnfd",
+                        "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                        "member-vnf-index": 1
+                    },
+                    {
+                        "name": "Weywot",
+                        "member-vdus": [
+                            {
+                                "member-vdu-ref": "iovdu_0"
+                            }
+                        ],
+                        "requirement": "Place this VM on the Kuiper belt object Weywot",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": [],
+                        "vnf-name": "pong_vnfd",
+                        "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                        "member-vnf-index": 2
+                    }
+                ]
+            },
+            {
+                "input-parameter-xpath": [
+                    {
+                        "xpath": "/nsd:nsd-catalog/nsd:nsd/nsd:vendor"
+                    }
+                ],
+                "ip-profiles": [
+                    {
+                        "name": "InterVNFLink",
+                        "description": "Inter VNF Link",
+                        "ip-profile-params": {
+                            "ip-version": "ipv4",
+                            "gateway-address": "31.31.31.210",
+                            "subnet-address": "31.31.31.0/24",
+                            "dhcp-params": {
+                                "enabled": "true"
+                            }
+                        }
+                    }
+                ],
+                "version": "1.0",
+                "initial-config-primitive": [
+                    {
+                        "seq": 1,
+                        "name": "start traffic",
+                        "parameter": [
+                            {
+                                "name": "userid"
+                            }
+                        ],
+                        "user-defined-script": "start_traffic.py"
+                    }
+                ],
+                "name": "a copy of pingpong",
+                "meta": {
+                    "containerPositionMap": {
+                        "1": {
+                            "top": 130,
+                            "left": 260,
+                            "right": 510,
+                            "bottom": 185,
+                            "width": 250,
+                            "height": 55
+                        },
+                        "2": {
+                            "top": 130,
+                            "left": 635,
+                            "right": 885,
+                            "bottom": 185,
+                            "width": 250,
+                            "height": 55
+                        },
+                        "e0d7f471-ade0-49d3-9f47-225aa724ae08": {
+                            "top": 30,
+                            "left": 135,
+                            "right": 385,
+                            "bottom": 85,
+                            "width": 250,
+                            "height": 55
+                        },
+                        "ping_pong_vld1": {
+                            "top": 300,
+                            "left": 447.5,
+                            "right": 697.5,
+                            "bottom": 338,
+                            "width": 250,
+                            "height": 38
+                        }
+                    },
+                    "instance-ref-count": 0
+                },
+                "short-name": "ping_pong_nsd",
+                "id": "e0d7f471-ade0-49d3-9f47-225aa724ae08",
+                "logo": "rift_logo.png",
+                "description": "Toy NS",
+                "constituent-vnfd": [
+                    {
+                        "member-vnf-index": 1,
+                        "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                        "start-by-default": "true",
+                        "name": "ping_vnfd",
+                        "vnf-name": "ping_vnfd"
+                    },
+                    {
+                        "member-vnf-index": 2,
+                        "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                        "start-by-default": "true",
+                        "name": "pong_vnfd",
+                        "vnf-name": "pong_vnfd"
+                    }
+                ],
+                "vendor": "RIFT.io",
+                "placement-groups": [
+                    {
+                        "member-vnfd": [
+                            {
+                                "member-vnf-index-ref": 1,
+                                "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                                "name": "ping_vnfd"
+                            },
+                            {
+                                "member-vnf-index-ref": 2,
+                                "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                                "name": "pong_vnfd"
+                            }
+                        ],
+                        "name": "Orcus",
+                        "requirement": "Place this VM on the Kuiper belt object Orcus",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": []
+                    },
+                    {
+                        "member-vnfd": [
+                            {
+                                "member-vnf-index-ref": 1,
+                                "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                                "name": "ping_vnfd"
+                            },
+                            {
+                                "member-vnf-index-ref": 2,
+                                "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                                "name": "pong_vnfd"
+                            }
+                        ],
+                        "name": "Quaoar",
+                        "requirement": "Place this VM on the Kuiper belt object Quaoar",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": []
+                    }
+                ],
+                "vld": [
+                    {
+                        "type": "ELAN",
+                        "description": "Toy VL",
+                        "version": "1.0",
+                        "vendor": "RIFT.io",
+                        "name": "ping_pong_vld",
+                        "short-name": "ping_pong_vld",
+                        "id": "ping_pong_vld1",
+                        "vnfd-connection-point-ref": [
+                            {
+                                "member-vnf-index-ref": 1,
+                                "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                                "vnfd-connection-point-ref": "ping_vnfd/cp0"
+                            },
+                            {
+                                "member-vnf-index-ref": 2,
+                                "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                                "vnfd-connection-point-ref": "pong_vnfd/cp0"
+                            }
+                        ]
+                    }
+                ],
+                "ns-placement-groups": [
+                    {
+                        "member-vnfd": [
+                            {
+                                "member-vnf-index-ref": 1,
+                                "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                                "name": "ping_vnfd"
+                            },
+                            {
+                                "member-vnf-index-ref": 2,
+                                "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                                "name": "pong_vnfd"
+                            }
+                        ],
+                        "name": "Orcus",
+                        "requirement": "Place this VM on the Kuiper belt object Orcus",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": []
+                    },
+                    {
+                        "member-vnfd": [
+                            {
+                                "member-vnf-index-ref": 1,
+                                "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                                "name": "ping_vnfd"
+                            },
+                            {
+                                "member-vnf-index-ref": 2,
+                                "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                                "name": "pong_vnfd"
+                            }
+                        ],
+                        "name": "Quaoar",
+                        "requirement": "Place this VM on the Kuiper belt object Quaoar",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": []
+                    }
+                ],
+                "vnf-placement-groups": [
+                    {
+                        "name": "Eris",
+                        "member-vdus": [
+                            {
+                                "member-vdu-ref": "iovdu_0"
+                            }
+                        ],
+                        "requirement": "Place this VM on the Kuiper belt object Eris",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": [],
+                        "vnf-name": "ping_vnfd",
+                        "vnfd-id-ref": "1b84ecbc-630c-11e6-9050-3eca272c7412",
+                        "member-vnf-index": 1
+                    },
+                    {
+                        "name": "Weywot",
+                        "member-vdus": [
+                            {
+                                "member-vdu-ref": "iovdu_0"
+                            }
+                        ],
+                        "requirement": "Place this VM on the Kuiper belt object Weywot",
+                        "strategy": "COLOCATION",
+                        "host-aggregate": [],
+                        "vnf-name": "pong_vnfd",
+                        "vnfd-id-ref": "1b859c48-630c-11e6-9050-3eca272c7412",
+                        "member-vnf-index": 2
+                    }
+                ]
+            }
+        ];
+}
diff --git a/skyquake/tests/stories/sq-input-slider.js b/skyquake/tests/stories/sq-input-slider.js
new file mode 100644
index 0000000..e4ebd31
--- /dev/null
+++ b/skyquake/tests/stories/sq-input-slider.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import { storiesOf, action } from '@kadira/storybook';
+import SqInputRangeSlider from '../../framework/widgets/sq-input-range-slider/sq-input-range-slider'
+import StyleGuideItem from 'react-style-guide';
+import '../../node_modules/react-style-guide/node_modules/highlight.js/styles/github.css';
+
+let containerStyle = {
+  display: 'flex'
+};
+
+storiesOf('SqInputRangeSlider', module)
+  .add('Horizontal Slider', () => (
+  <div>
+    <div className="inputcontainer" style={containerStyle}>
+      <SqInputRangeSlider className="one two three" max={150} step={10} width={300} onChange={action('valueChanged')} />
+    </div>
+    <div className="inputcontainer" style={containerStyle}>
+      <SqInputRangeSlider className="one two three" max={150} step={10} width={800} onChange={action('valueChanged')} />
+    </div>
+    <div className="inputcontainer" style={containerStyle}>
+      <SqInputRangeSlider className="one two three" max={150} step={10} width={200} disabled onChange={action('valueChanged')} />
+    </div>
+  </div>
+  ))
+  .add('Vertical Slider', () => (
+                                 <StyleGuideItem>
+<div style={{display:'flex'}}>
+  <div style={containerStyle}>
+    <SqInputRangeSlider vertical={true} width={300}  onChange={action('valueChanged')} />
+  </div>
+  <div style={containerStyle}>
+    <SqInputRangeSlider vertical={true} width={600}  onChange={action('valueChanged')} />
+  </div>
+  <div style={containerStyle}>
+    <SqInputRangeSlider vertical={true} width={300}  onChange={action('valueChanged')} disabled={true} />
+    <SqInputRangeSlider vertical={true} width={300}  onChange={action('valueChanged')} disabled={true} />
+  </div>
+</div>
+</StyleGuideItem>
+  ));
diff --git a/skyquake/tests/stories/sshKeyCard.js b/skyquake/tests/stories/sshKeyCard.js
new file mode 100644
index 0000000..649ec4a
--- /dev/null
+++ b/skyquake/tests/stories/sshKeyCard.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { storiesOf, action } from '@kadira/storybook';
+import DashboardCard from '../../framework/widgets/dashboard_card/dashboard_card.jsx'
+import SshKeyCard from '../../plugins/launchpad/src/ssh_keys/sshKeyCard.jsx';
+import SshKeys from '../../plugins/launchpad/src/ssh_keys/sshKeys.jsx';
+import SshKeyStore from '../../plugins/launchpad/src/ssh_keys/sshKeyStore.js';
+import Alt from '../../framework/widgets/skyquake_container/skyquakeAltInstance.js'
+import reactToJsx from 'react-to-jsx';
+import '../../node_modules/open-iconic/font/css/open-iconic.css';
+import 'style/base.scss';
+// import StyleGuideItem from 'react-style-guide';
+// import '../../node_modules/react-style-guide/node_modules/highlight.js/styles/github.css';
+
+const Store = Alt.createStore(SshKeyStore)
+
+storiesOf('CatalogCard', module)
+// .add('page', () => (<SshKeys />))
+.add('sshKeyPage', () => (<Test />))
+
+
+class Test extends React.Component {
+  constructor() {
+    super();
+    let self = this;
+    this.state = Store.getState();
+    console.log(this.state)
+    Store.listen(function(data) {
+      self.setState({data: data.data})
+    })
+  }
+  render() {
+    let self = this;
+    return (
+      <DashboardCard>
+        {
+          self.state.data && self.state.data.keys.map(function(k, i) {
+            let sshKey = self.state.data.entities[k];
+            return (
+              <SshKeyCard key={i}  name={sshKey.name} value={sshKey.key}
+              editMode={sshKey.isEditable}
+              editKey= {Store.editSshKeyPair(sshKey.name)}
+              updateSshKeyPair={Store.updateSshKeyPair}
+              cancelEditSshKeyPair={Store.cancelEditSshKeyPair}
+              saveEditSshKeyPair={Store.saveEditSshKeyPair(sshKey.name)}
+              />
+            )
+          })
+        }
+      </DashboardCard>
+    )
+  }
+}
diff --git a/skyquake/tests/support/babel.js b/skyquake/tests/support/babel.js
new file mode 100644
index 0000000..c8f0b73
--- /dev/null
+++ b/skyquake/tests/support/babel.js
@@ -0,0 +1,46 @@
+define([
+    'intern/dojo/request',
+    'intern/dojo/node!babel-core',
+    'intern/dojo/node!react'
+], function (request, babel, react) {
+    /**
+     * React has AMD support so when require is present it will behave as a module
+     * The react example however expects a global React so we need to put it back
+     * into global space.
+     */
+    function globalizeReact() {
+        var global = (function () {
+            return this;
+        })();
+        global.React = global.React || react;
+    }
+
+    return {
+        /**
+         * A function that is called to load a resource.
+         *
+         * @param name The name of the resource to load.
+         * @param req A local "require" function to use to load other modules.
+         * @param onload A function to call with the value for name. This tells the loader that the plugin is done
+         *        loading the resource. onload.error() can be called, passing an error object to it, if the plugin
+         *        detects an error condition that means the resource will fail to load correctly.
+         */
+        load: function (name, req, onload) {
+            globalizeReact();
+
+            request(req.toUrl(name)).then(function (sourceCode) {
+                // Compile the JSX source into JavaScript code
+                var javascriptCode = babel.transform(sourceCode,{presets:['es2015', 'react']}).code;
+
+                // Execute the compiled function. In this case the example code
+                // puts things into the global space so it needs to be run in a script tag.
+                var codeNode = document.createTextNode(javascriptCode);
+                var node = document.createElement('script');
+                node.type = 'text/javascript';
+                node.appendChild(codeNode);
+                document.head.appendChild(node);
+                onload();
+            });
+        }
+    };
+});
diff --git a/skyquake/tests/unit/plugin_discoverer.js b/skyquake/tests/unit/plugin_discoverer.js
new file mode 100644
index 0000000..337f2fe
--- /dev/null
+++ b/skyquake/tests/unit/plugin_discoverer.js
@@ -0,0 +1,62 @@
+define(function (require) {
+    var base_plugins = ['about', 'accounts', 'composer', 'debug', 'goodbyeworld', 'helloworld', 'launchpad', 'logging'];
+    var registerSuite = require('intern!object');
+    var assert = require('intern/chai!assert');
+    var plugin_discoverer = require('intern/dojo/node!../../framework/core/modules/plugin_discoverer.js');
+    var skyquakeEmitter = require('intern/dojo/node!../../framework/core/modules/skyquakeEmitter.js');
+    var _ = require('intern/dojo/node!lodash');
+    var fs = require('intern/dojo/node!fs');
+    registerSuite({
+        name: 'plugin_discoverer',
+        before: function() {
+
+        },
+        after: function() {
+            // Exit process.
+            // TODO: Should cleanup plugin_discoverer instead and call that
+            setTimeout(function() {
+                var path = process.cwd() + '/plugins/helloworld/test.txt';
+                fs.unlinkSync(path);
+                process.exit(0);
+            });
+        },
+        'Test init': function () {
+            var res = plugin_discoverer.init();
+            assert.isUndefined(res, 'return value is undefined');
+        },
+        'Test config': function() {
+            var path = process.cwd() + '/plugins';
+            var res = plugin_discoverer.config({
+                plugins_path: path
+            });
+            assert.isUndefined(res, 'return value is undefined');
+        },
+        'Test run plugin add discovery': function() {
+            var deferred = this.async();
+
+            var plugins_detected = [];
+
+            skyquakeEmitter.on('plugin_discoverer.plugin_discovered', function(plugin_name) {
+                plugins_detected.push(plugin_name);
+                if (_.intersection(plugins_detected, base_plugins).length == 8 ) {
+                    // all expected plugins were discovered
+                    deferred.resolve('All expected plugins discovered');
+                }
+            });
+
+            var res = plugin_discoverer.run();
+        },
+        'Test run plugin update discovery': function() {
+            var deferred = this.async();
+
+            setTimeout(function() {
+                var path = process.cwd() + '/plugins/helloworld/test.txt';
+                fs.openSync(path, 'a+');
+            });
+
+            skyquakeEmitter.on('plugin_discoverer.plugin_updated', function(plugin_name) {
+                deferred.resolve(); 
+            });
+        },
+    });
+});
diff --git a/skyquake/tests/unit/router.js b/skyquake/tests/unit/router.js
new file mode 100644
index 0000000..3f30831
--- /dev/null
+++ b/skyquake/tests/unit/router.js
@@ -0,0 +1,20 @@
+// import SkyquakeRouter from './../../framework/widgets/skyquake_container/skyquakeRouter.jsx';
+
+define([
+        'intern!object',
+        'intern/chai!assert',
+        'require',
+        './../support/babel!../../framework/widgets/skyquake_container/skyquakeRouter.jsx'
+       ],
+
+    function (registerSuite, assert, require, skyquakeRouter) {
+        console.log(skyquakeRouter)
+    registerSuite({
+        name: 'plugin_discoverer',
+
+        init: function () {
+            // var res = plugin_discoverer.init();
+            // assert.isUndefined(res, 'return value is undefined');
+        }
+    });
+});