From 51cc91662dbc8080e8cf2c3620147900d973534e Mon Sep 17 00:00:00 2001 From: James Kafader Date: Tue, 3 Dec 2024 13:35:43 -0800 Subject: [PATCH 1/2] rename steps in Makefile to remove colons, some makefile cleanup --- .github/workflows/ci.yml | 4 ++-- .npmignore | 3 ++- Makefile | 31 ++++++++++--------------------- 3 files changed, 14 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b0ccfbd..cbf637a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,7 +44,7 @@ jobs: run: yarn build - name: Test components - run: make test:component + run: make test-component - name: Install E2E dependencies run: | @@ -57,7 +57,7 @@ jobs: - name: E2E testing run: | - make test:e2e + make test-e2e - name: Check for backend id: check-for-backend diff --git a/.npmignore b/.npmignore index 72ec715..4cd8bf1 100644 --- a/.npmignore +++ b/.npmignore @@ -11,4 +11,5 @@ yarn-error.log .vitest playwright-report .auth -test-results \ No newline at end of file +test-results +test/dist \ No newline at end of file diff --git a/Makefile b/Makefile index 9a8f5ab..af59d31 100644 --- a/Makefile +++ b/Makefile @@ -17,13 +17,7 @@ prod: make build .PHONY: build -build: - yarn test - yarn test:react - @echo "Waiting for container to spin up..." - @sleep $(SPINUP_SLEEP_T) - @echo "Starting E2E Tests..." - yarn e2e +build: test yarn build yarn sign "--rootUrls" https://dashboard.stardust.es.net/,https://gf.gc1.dev-stage.stardust.es.net/ yarn run "build_dts" @@ -47,32 +41,27 @@ compose: # get instances info sleep 2 docker inspect $(CONTAINER_NAME) > $(PROJECT_DIR)/e2e/grafana-docker.json + .PHONY: test -test: compose - @echo "Starting component tests..." - yarn test - yarn test:react - @echo "Waiting for container to spin up..." - @sleep $(SPINUP_SLEEP_T) - @echo "Starting E2E Tests..." - yarn e2e +test: compose test-component test-e2e -.PHONY: test\:component -test\:component: +.PHONY: test-component +test-component: @echo "Starting component tests..." yarn test yarn test:react + rm -R test/dist/* -.PHONY: test\:e2e -test\:e2e: compose +.PHONY: test-e2e +test-e2e: compose # run e2e tests @echo "Waiting for container to spin up..." @sleep $(SPINUP_SLEEP_T) @echo "Starting E2E Tests..." yarn run e2e -.PHONY: test\:ui -test\:ui: compose +.PHONY: test-ui +test-ui: compose # run e2e tests, but with ui @echo "Starting E2E tests in Playwright UI..." yarn run e2e:ui From 5abc0ae09049ed73e098bd9bf636a4ffd4128e3a Mon Sep 17 00:00:00 2001 From: James Kafader Date: Thu, 19 Dec 2024 13:47:17 -0800 Subject: [PATCH 2/2] add tests for node matching. Add ability to toggle sidebar even if we're working with a full configuration from remote URL --- index.html | 1 + node_matching.html | 162 ++++++++++++++++++++++++++ src/components/MapCanvas.component.js | 7 ++ src/components/SideBar.component.js | 2 +- src/components/lib/pubsub.js | 8 +- src/module.ts | 1 - test/module.spec.js | 59 ++++++++++ 7 files changed, 237 insertions(+), 3 deletions(-) create mode 100644 node_matching.html diff --git a/index.html b/index.html index 05a2a30..e859c78 100644 --- a/index.html +++ b/index.html @@ -17,6 +17,7 @@

Embedded Map Demonstration page

Edit Mode Off        Autodetect Demo + Node Traffic-matching Demo diff --git a/node_matching.html b/node_matching.html new file mode 100644 index 0000000..e2314ff --- /dev/null +++ b/node_matching.html @@ -0,0 +1,162 @@ + + + Embedded Map Demonstration Page + + + + + + + + + + + + + + + + + + + + + + + +
+

Demonstration Page: Traffic Matching by Node Only

+
+ Main Demonstration Page   +
+
+ + +
+
Map DataMap Options
+ + + + + +
+ + + + \ No newline at end of file diff --git a/src/components/MapCanvas.component.js b/src/components/MapCanvas.component.js index 5e95b82..d0220e4 100644 --- a/src/components/MapCanvas.component.js +++ b/src/components/MapCanvas.component.js @@ -137,6 +137,7 @@ export class MapCanvas extends BindableHTMLElement { "enableNodeAnimation": "masked", "enableScrolling": "masked", "showViewControls": "masked", + "showSidebar": "masked", "thresholds": "masked", "multiLayerNodeSnap": "masked", "configurationUrl": "masked", @@ -472,6 +473,12 @@ export class MapCanvas extends BindableHTMLElement { } // set the traffic sample by single-point match if no match yet. Use forward directionality if(!targetEdge){ targetEdge = layerTopology.edges[edgeHash[singleIdSelector]] } + // if there's no matching edge, we won't find one. + // still, we must check for the case that we can match this datapoint to a node. + if(!targetEdge && nodeHash.hasOwnProperty(singleIdSelector)){ + // in this case, create a dummy edge so we can still do node traffic accounting. + targetEdge = { nodeA: singleIdSelector } + } // if no match at all, don't mark up this edge with match data. if(!targetEdge){ return } targetEdge.azValue = row[values.in]; diff --git a/src/components/SideBar.component.js b/src/components/SideBar.component.js index fe13296..b3b9954 100644 --- a/src/components/SideBar.component.js +++ b/src/components/SideBar.component.js @@ -73,7 +73,7 @@ class SideBar extends BindableHTMLElement { render() { if (!this.shadow) { this.shadow = document.createElement("div"); - this.shadow.setAttribute("class", "tight-form-func"); + this.shadow.setAttribute("class", "tight-form-func esmap-sidebar"); this.shadow.id = "sidebar-"+this.instanceId; this.append(this.shadow); } diff --git a/src/components/lib/pubsub.js b/src/components/lib/pubsub.js index 4185f1d..8fd59f7 100644 --- a/src/components/lib/pubsub.js +++ b/src/components/lib/pubsub.js @@ -46,7 +46,13 @@ const doPublish = function(topic, eventData, messageBus) { console.debug("publishing event on topic", topic, scopeMessage); } var accessor = `${messageBus.instanceId}.lastEvents.${topic}`; - localStorage.setItem(accessor, JSON.stringify(eventData)); + try { + localStorage.setItem(accessor, JSON.stringify(eventData)); + } catch(e) { + console.error(e); + console.log("received error saving to localStorage. clearing localStorage."); + localStorage.clear(); + } var subscriberData = messageBus.topics[topic]; if (!subscriberData) return; var subscribers = Object.values(subscriberData); diff --git a/src/module.ts b/src/module.ts index 045c5cf..6265903 100644 --- a/src/module.ts +++ b/src/module.ts @@ -580,7 +580,6 @@ const options = { name: 'Show Map Sidebar', description: 'Show sidebar. If hidden, tooltips will appear on hover.', category: "View Options", - showIf: { "topologySource": ["autodetect", "json", "layerurls"] }, defaultValue: true, }, diff --git a/test/module.spec.js b/test/module.spec.js index 12f19bb..c8ef3cd 100644 --- a/test/module.spec.js +++ b/test/module.spec.js @@ -1697,4 +1697,63 @@ describe( "Class MapCanvas", () => { let node = canvas.querySelector(".node"); expect(node).toBeTruthy(); }) + it("should allow users to show and hide the sidebar even when loading the full configuration from a url", ()=>{ + let canvas = document.querySelector("esnet-map-canvas"); + let newOptions = JSON.parse(JSON.stringify(canvas.options)); + + // this data url is a valid base-64 encoded terranova-style response. + let targetUrl = "data:text/plain;charset=utf-8;base64,ew0KICAiaW5pdGlhbFZpZXdTdHJhdGVneSI6ICJzdGF0aWMiLA0KICAibGF0aXR1ZGVWYXIiOiBudWxsLA0KICAibG9uZ2l0dWRlVmFyIjogbnVsbCwNCiAgInZpZXdwb3J0Ijogew0KICAgICJjZW50ZXIiOiB7DQogICAgICAibGF0IjogMzguOTU5LA0KICAgICAgImxuZyI6IC05Ny45OTgNCiAgICB9LA0KICAgICJ0b3AiOiBudWxsLA0KICAgICJsZWZ0IjogbnVsbCwNCiAgICAiYm90dG9tIjogbnVsbCwNCiAgICAicmlnaHQiOiBudWxsLA0KICAgICJ6b29tIjogNA0KICB9LA0KICAiYmFja2dyb3VuZCI6ICIjREREREREIiwNCiAgInRpbGVzZXQiOiB7DQogICAgImdlb2dyYXBoaWMiOiAiYXJjZ2lzIiwNCiAgICAiYm91bmRhcmllcyI6IG51bGwsDQogICAgImxhYmVscyI6IG51bGwNCiAgfSwNCiAgImVkaXRNb2RlIjogZmFsc2UsDQogICJzaG93U2lkZWJhciI6IGZhbHNlLA0KICAic2hvd1ZpZXdDb250cm9scyI6IHRydWUsDQogICJzaG93TGVnZW5kIjogZmFsc2UsDQogICJsZWdlbmRDb2x1bW5MZW5ndGgiOiAzLA0KICAibGVnZW5kUG9zaXRpb24iOiAiYm90dG9tTGVmdCIsDQogICJsZWdlbmREZWZhdWx0QmVoYXZpb3IiOiAibWluaW1pemVkIiwNCiAgImVuYWJsZVNjcm9sbGluZyI6IHRydWUsDQogICJlbmFibGVFZGl0aW5nIjogdHJ1ZSwNCiAgImVuYWJsZU5vZGVBbmltYXRpb24iOiB0cnVlLA0KICAiZW5hYmxlRWRnZUFuaW1hdGlvbiI6IHRydWUsDQogICJ0aHJlc2hvbGRzIjogW10sDQogICJ6SW5kZXhCYXNlIjogMTAsDQogICJsYXllcnMiOiBbDQogICAgew0KICAgICAgInZpc2libGUiOiB0cnVlLA0KICAgICAgIm5hbWUiOiAiTGF5ZXIgMSIsDQogICAgICAiY29sb3IiOiAiI0FBQUFGRiIsDQogICAgICAiZWRnZVdpZHRoIjogMi41LA0KICAgICAgInBhdGhPZmZzZXQiOiAxLjc1LA0KICAgICAgIm5vZGVXaWR0aCI6IDYuNSwNCiAgICAgICJqc29uRnJvbVVybCI6IGZhbHNlLA0KICAgICAgIm1hcGpzb24iOiAieyJub2RlcyI6W3sibmFtZSI6IkIiLCJjb2xvciI6IiNBQUFBRkYiLCJtZXRhIjp7ImRpc3BsYXlfbmFtZSI6IiIsInN2ZyI6IjxzdmcgeT1cIi01XCIgeD1cIi01XCIgd2lkdGg9XCIxMFwiIGhlaWdodD1cIjEwXCIgdmlld0JveD1cIi01LjUgLTUuNSAxMSAxMVwiPjxjaXJjbGUgcj1cIjVcIj48L2NpcmNsZT48L3N2Zz48dGV4dCBzdHlsZT1cImZvbnQtc2l6ZToxMnB4OyBmaWx0ZXI6IGRyb3Atc2hhZG93KDBweCAwcHggMXB4IHJnYmEoMjU1LDI1NSwyNTUsMS4wKSk7XCIgc3Ryb2tlPVwibm9uZVwiIGZpbGw9XCIjMTExMTExXCIgeT1cIjNcIiB4PVwiLTE4XCI+QjwvdGV4dD4iLCJ0ZW1wbGF0ZSI6IiJ9LCJjb29yZGluYXRlIjpbNDQuMDg4LC0xMDguNjMzXSwiY2hpbGRyZW4iOltdLCJzb3J0IjowLCJwYXJlbnRzIjpbXX0seyJuYW1lIjoiQSIsImNvbG9yIjoiI0FBQUFGRiIsIm1ldGEiOnsiZGlzcGxheV9uYW1lIjoiIiwic3ZnIjoiPHN2ZyB5PVwiLTVcIiB4PVwiLTVcIiB3aWR0aD1cIjEwXCIgaGVpZ2h0PVwiMTBcIiB2aWV3Qm94PVwiLTUuNSAtNS41IDExIDExXCI+PGNpcmNsZSByPVwiNVwiPjwvY2lyY2xlPjwvc3ZnPjx0ZXh0IHN0eWxlPVwiZm9udC1zaXplOjEycHg7IGZpbHRlcjogZHJvcC1zaGFkb3coMHB4IDBweCAxcHggcmdiYSgyNTUsMjU1LDI1NSwxLjApKTtcIiBzdHJva2U9XCJub25lXCIgZmlsbD1cIiMxMTExMTFcIiB5PVwiM1wiIHg9XCI4XCI+QTwvdGV4dD4iLCJ0ZW1wbGF0ZSI6IiJ9LCJjb29yZGluYXRlIjpbNDMuODM1LC05Mi44MTNdLCJjaGlsZHJlbiI6W10sInNvcnQiOjAsInBhcmVudHMiOltdfSx7Im5hbWUiOiJDIiwiY29sb3IiOiIjQUFBQUZGIiwibWV0YSI6eyJkaXNwbGF5X25hbWUiOiIiLCJzdmciOiI8c3ZnIHk9XCItNVwiIHg9XCItNVwiIHdpZHRoPVwiMTBcIiBoZWlnaHQ9XCIxMFwiIHZpZXdCb3g9XCItNS41IC01LjUgMTEgMTFcIj48Y2lyY2xlIHI9XCI1XCI+PC9jaXJjbGU+PC9zdmc+PHRleHQgc3R5bGU9XCJmb250LXNpemU6MTJweDsgZmlsdGVyOiBkcm9wLXNoYWRvdygwcHggMHB4IDFweCByZ2JhKDI1NSwyNTUsMjU1LDEuMCkpO1wiIHN0cm9rZT1cIm5vbmVcIiBmaWxsPVwiIzExMTExMVwiIHk9XCIzXCIgeD1cIjhcIj5DPC90ZXh0PiIsInRlbXBsYXRlIjoiIn0sImNvb3JkaW5hdGUiOlszNC40NTIsLTEwMC4xOTVdLCJjaGlsZHJlbiI6W10sInNvcnQiOjAsInBhcmVudHMiOltdfV0sImVkZ2VzIjpbeyJuYW1lIjoiQS0tQiIsIm1ldGEiOnsiZW5kcG9pbnRfaWRlbnRpZmllcnMiOnsibmFtZXMiOlsiQSIsIkIiXX19LCJsYXllciI6IjAiLCJhekNvbG9yIjoiI0FBQUFGRiIsInphQ29sb3IiOiIjQUFBQUZGIiwiY29vcmRpbmF0ZXMiOltbNDMuODM0NTI2NzgyMjM2ODQsLTkyLjgxMjUwMDAwMDAwMDAxXSxbNDQuMDg3NTg1MDI4MjQ1MTgsLTEwOC42MzI4MTI1MDAwMDAwMV1dLCJjaGlsZHJlbiI6W10sIm5vZGVBIjoiQSIsIm5vZGVaIjoiQiIsInBvaW50cyI6W1s2MDMsMjQxXSxbNjkzLDI0M11dLCJyZWplY3RlZCI6MCwiY29udHJvbFBvaW50UGF0aCI6Ik02OTMsMjQzTDYwMywyNDEiLCJhelBhdGgiOiJNNjkyLjk3Nzc4MzI2MjcxNDksMjQzLjk5OTc1MzE3NzgzMTYyTDYwMi45NjY2Nzk2OTE1OTQ0LDI0MS45OTk0NDQ3MjQzNTgzNyIsInphUGF0aCI6Ik02MDMuMDIyMjE2NzM3Mjg1MSwyNDAuMDAwMjQ2ODIyMTY4MzhMNjkzLjAzMzMyMDMwODQwNTYsMjQyLjAwMDU1NTI3NTY0MTYzIiwic29ydCI6MX0seyJuYW1lIjoiQS0tQyIsIm1ldGEiOnsiZW5kcG9pbnRfaWRlbnRpZmllcnMiOnsibmFtZXMiOlsiQSIsIkMiXX19LCJsYXllciI6IjAiLCJhekNvbG9yIjoiI0FBQUFGRiIsInphQ29sb3IiOiIjQUFBQUZGIiwiY29vcmRpbmF0ZXMiOltbNDMuODM0NTI2NzgyMjM2ODQsLTkyLjgxMjUwMDAwMDAwMDAxXSxbMzQuNDUyMjE4NDcyODI2NTY2LC0xMDAuMTk1MzEyNTAwMDAwMDFdXSwiY2hpbGRyZW4iOltdLCJub2RlQSI6IkEiLCJub2RlWiI6IkMiLCJwb2ludHMiOltbNjUxLDMxMl0sWzY5MywyNDNdXSwicmVqZWN0ZWQiOjAsImNvbnRyb2xQb2ludFBhdGgiOiJNNjkzLDI0M0w2NTEsMzEyIiwiYXpQYXRoIjoiTTY5My44NTQxOTg1NTU2MTQ0LDI0My41MTk5NDY5NDY4OTU3NEw2NTEuODQ3Njk2ODE4ODgzMywzMTIuNTMwNDgxMDExMjEwODQiLCJ6YVBhdGgiOiJNNjUwLjE0NTgwMTQ0NDM4NTYsMzExLjQ4MDA1MzA1MzEwNDI2TDY5Mi4xNTIzMDMxODExMTY3LDI0Mi40Njk1MTg5ODg3ODkxMyIsInNvcnQiOjF9LHsibmFtZSI6IkItLUMiLCJtZXRhIjp7ImVuZHBvaW50X2lkZW50aWZpZXJzIjp7Im5hbWVzIjpbIkIiLCJDIl19fSwibGF5ZXIiOiIwIiwiYXpDb2xvciI6IiNBQUFBRkYiLCJ6YUNvbG9yIjoiI0FBQUFGRiIsImNvb3JkaW5hdGVzIjpbWzQ0LjA4NzU4NTAyODI0NTE4LC0xMDguNjMyODEyNTAwMDAwMDFdLFszNC40NTIyMTg0NzI4MjY1NjYsLTEwMC4xOTUzMTI1MDAwMDAwMV1dLCJjaGlsZHJlbiI6W10sIm5vZGVBIjoiQiIsIm5vZGVaIjoiQyIsInBvaW50cyI6W1s2NTEsMzEyXSxbNjAzLDI0MV1dLCJyZWplY3RlZCI6MCwiY29udHJvbFBvaW50UGF0aCI6Ik02MDMsMjQxTDY1MSwzMTIiLCJhelBhdGgiOiJNNjAzLjgyODQ0MjY0ODk1MDcsMjQwLjQzOTkyNjA5NjQ4NDA0TDY1MS44MzQ5MjA4NzI3NDUzLDMxMS40NDk2MzAwMDA1ODY3IiwiemFQYXRoIjoiTTY1MC4xNzE1NTczNTEwNDkzLDMxMi41NjAwNzM5MDM1MTU5Nkw2MDIuMTY1MDc5MTI3MjU0NywyNDEuNTUwMzY5OTk5NDEzMyIsInNvcnQiOjF9XSwibGF5ZXIiOiJ0YWlsIiwibmFtZSI6Ik51bGwgRGF0YXNldCIsInBhdGhMYXlvdXQiOnsidHlwZSI6ImN1cnZlQ2FyZGluYWwiLCJ0ZW5zaW9uIjowLjZ9fSIsDQogICAgICAibWFwanNvblVybCI6ICJodHRwczovL3RlcnJhbm92YS1pbnRlcm5hbC5lcy5uZXQvdGVycmFub3ZhL2FwaS92MS9vdXRwdXQvZGF0YXNldC9MbGtkQ3VZL2dlb2dyYXBoaWMvbGl2ZS8/dGVtcGxhdGU9WGNqNmV5WCIsDQogICAgICAiZW5kcG9pbnRJZCI6ICJuYW1lcyIsDQogICAgICAiaW5ib3VuZFZhbHVlRmllbGQiOiAiYXRveiIsDQogICAgICAib3V0Ym91bmRWYWx1ZUZpZWxkIjogInp0b2EiLA0KICAgICAgInNyY0ZpZWxkIjogInNyYyIsDQogICAgICAiZHN0RmllbGQiOiAiZHN0IiwNCiAgICAgICJsZWdlbmQiOiB0cnVlLA0KICAgICAgIm5vZGVIaWdobGlnaHQiOiAiI0FBRkZBQSINCiAgICB9DQogIF0NCn0="; + + newOptions.configurationUrl = targetUrl; + newOptions.showSidebar = true; + + canvas.setOptions(newOptions); + + let node = canvas.querySelector(".esmap-sidebar"); + expect(node).toBeTruthy(); + + newOptions = JSON.parse(JSON.stringify(canvas.options)); + newOptions.showSidebar = false; + + canvas.setOptions(newOptions); + + node = canvas.querySelector(".esmap-sidebar"); + expect(node).not.toBeTruthy(); + }); + it("should color nodes when there's a matching edge datapoint, even without a matching edge", ()=>{ + let canvas = document.querySelector("esnet-map-canvas"); + let newOptions = JSON.parse(JSON.stringify(canvas.options)); + + newOptions.layers[0].nodeThresholds = [{"value": 0, "color": "#E04040"}, {"value": 200000000, "color": "#5bb436"}] + let traffic = [ + {"src_latitude":39,"src_longitude":-98,"src_name":"NodeA","in_bits": 16000000, "out_bits": 160000000}, + {"src_latitude":42,"src_longitude":-102,"src_name":"NodeB","in_bits": 320000000, "out_bits": 32000000}, + ]; + let topology = [{ + "edges": [], + "nodes": [ + { "name": "NodeA", "coordinate": [39, -98], "meta": { + "svg": "NodeA" + } + }, + { "name": "NodeB", "coordinate": [42, -102], "meta": { + "svg": "NodeB" + } + } + ] + }]; + + canvas.setOptions(newOptions); + canvas.setTopology(topology); + canvas.setTraffic(traffic); + + let nodeA = canvas.querySelector(".node-NodeA circle"); + expect(nodeA).toBeTruthy(); + let style = getComputedStyle(nodeA); + expect(style.fill).toEqual("rgb(224, 64, 64)"); + let nodeB = canvas.querySelector(".node-NodeB circle"); + expect(nodeB).toBeTruthy(); + style = getComputedStyle(nodeB); + expect(style.fill).toEqual("rgb(91, 180, 54)") + }) } ); \ No newline at end of file