Skip to content

Commit

Permalink
Merge branch 'end-to-end-tests' of github.com:esnet/grafana-esnet-net…
Browse files Browse the repository at this point in the history
…workmap-panel into test/TERR-425-react-component-code-coverage
  • Loading branch information
sanchezelton committed Oct 14, 2024
2 parents a0556bf + 74c5ba9 commit ccffeb1
Show file tree
Hide file tree
Showing 19 changed files with 151 additions and 69 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
steps:
- uses: actions/checkout@v2

- name: Setup Node.js environment for build
- name: Setup Node.js environment for build and testing
uses: actions/[email protected]
with:
node-version: "18.x"
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ e2e-results/
.idea
.vscode
.DS_store
*.code-workspace

# vscode
*.code-workspace
Expand All @@ -51,3 +52,6 @@ tests-examples

# autogenerated by Makefile for e2e
e2e/grafana-docker.json

# artifacts from github
.config/env
21 changes: 4 additions & 17 deletions demonstration/dashboard.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,35 +89,22 @@
"initialViewStrategy": "static",
"layers": [
{
"color": "#49a7f25e",
"dashboardEdgeDstVar": "destination",
"dashboardEdgeDstVarL1": "dest",
"color": "grey",
"dashboardEdgeDstVar": "dest",
"dashboardEdgeSrcVar": "source",
"dashboardEdgeSrcVarL1": "source",
"dashboardNodeVar": "node",
"dataFieldLabel": "Volume:",
"dstField": "dst",
"dstFieldLabel": "To:",
"edgeWidth": 2.5,
"edgeWidth": 3,
"endpointId": "pops",
"inboundValueField": "in_bits",
"legend": true,
"mapjson": "{\"edges\":[{\"name\":\"A--B\",\"meta\":{\"endpoint_identifiers\":{\"pops\":[\"A\",\"B\"]}},\"coordinates\":[[42.30619185993376,-106.00734642032997],[42.30619185993376,-92.30145333497663]],\"children\":[]},{\"name\":\"A--C\",\"meta\":{\"endpoint_identifiers\":{\"pops\":[\"A\",\"C\"]}},\"coordinates\":[[42.30619185993376,-106.00734642032997],[34.5952660320894,-99.10139879776413]],\"children\":[]},{\"name\":\"B--C\",\"meta\":{\"endpoint_identifiers\":{\"pops\":[\"B\",\"C\"]}},\"coordinates\":[[42.30619185993376,-92.30145333497663],[34.5952660320894,-99.10139879776413]],\"children\":[]}],\"nodes\":[{\"name\":\"A\",\"meta\":{\"display_name\":\"\",\"svg\":\"\",\"template\":\"\"},\"coordinate\":[42.30619185993376,-106.00734642032997]},{\"name\":\"B\",\"meta\":{\"display_name\":\"\",\"svg\":\"\",\"template\":\"\"},\"coordinate\":[42.30619185993376,-92.30145333497663]},{\"name\":\"C\",\"meta\":{\"display_name\":\"\",\"svg\":\"\",\"template\":\"\"},\"coordinate\":[34.5952660320894,-99.10139879776413]}]}",
"name": "Layer 1",
"nodeHighlight": "dark-blue",
"nodeNameMatchField": "Node",
"nodeThresholds": {
"mode": "absolute",
"steps": [
{
"color": "red"
},
{
"color": "dark-blue",
"value": 1
}
]
},
"nodeThresholds": "",
"nodeValueField": "Up/Down (1: up, 0:down)",
"nodeWidth": 5,
"outboundValueField": "out_bits",
Expand Down
2 changes: 1 addition & 1 deletion dist/components/css/esmap.css.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export const esmapCss: "\n#${instanceId} .leaflet-pane svg path.edge {\n pointer-events: stroke;\n}\n#${instanceId} .leaflet-pane svg path.control {\n pointer-events: stroke;\n}\n\n#${instanceId} .map-panel {\n position: relative;\n}\n\n#${instanceId} svg path.edge-az {\n marker-start: url(\"#arrow\");\n}\n\n#${instanceId} svg path.edge-za {\n marker-start: url(\"#arrow\");\n}\n\n#${instanceId} svg circle.node {\n /* fill: #999; */\n stroke: #777;\n stroke-width: 1;\n pointer-events: all;\n}\n\n#${instanceId} svg path.edge {\n stroke-linecap: butt;\n fill: none;\n pointer-events: visiblePainted !important;\n}\n\n#${instanceId} svg path.animated-edge.edge-az {\n stroke: transparent;\n stroke-linecap: butt;\n fill: none;\n cursor: crosshair;\n}\n\n#${instanceId} svg path.animated-edge.edge-za {\n stroke: transparent;\n stroke-linecap: butt;\n fill: none;\n cursor: crosshair;\n}\n\n#${instanceId} svg path.animated-edge.edge-az.selected {\n}\n\n#${instanceId} svg path.animated-edge.edge-za.selected {\n}\n\n@keyframes dash {\n to {\n stroke-dashoffset: 0;\n }\n}\n\n\n#${instanceId} svg path.control {\n stroke-dasharray: 8 1;\n stroke-width: 6;\n stroke: #f808;\n fill: none;\n cursor: crosshair;\n}\n\n#${instanceId} svg circle.controlPoint {\n stroke: black;\n stroke-width: 1;\n fill: #f80;\n cursor: move;\n}\n\n#${instanceId} div.tooltip-hover {\n position:absolute;\n border-radius:4px;\n padding:10px;\n margin:10px;\n max-width:250px;\n box-shadow: 3px 3px 4px rgba(0, 0, 0, 0.2);\n font-family: sans-serif;\n}\n\n#${instanceId} div.tooltip-hover p:first-of-type {\n margin-top:0;\n}\n\n#${instanceId} div.tooltip-hover p {\n margin-top: 6px;\n margin-bottom:0;\n}\n\n#${instanceId} .legend-text {\n padding-left: 5px;\n vertical-align: middle;\n}\n\n\n\n#${instanceId} .home-overlay {\n position: absolute;\n margin-top: 12px;\n margin-left: 57px;\n}\n#${instanceId} .home-overlay > .button {\n border-radius: 4px;\n padding: 5px 10px;\n margin-right: 5px;\n box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.5);\n display: inline-block;\n cursor: pointer;\n}\n\n#${instanceId} .legend {\n position: absolute;\n padding: 1em 1em 0.3em 1em;\n margin: 0.8em;\n box-shadow: 0px 0px 2px rgb(0 0 0 / 50%);\n border-radius: 3px;\n}\n\n#${instanceId} .legend.topright {\n top: 0;\n right: 0;\n}\n\n#${instanceId} .legend.bottomright {\n bottom: 0;\n right: 0;\n margin: 0.8em 0.8em 1.8em 0.8em;\n}\n\n#${instanceId} .legend.bottomleft {\n bottom: 0;\n left: 0;\n}\n\n#${instanceId} .color-sample {\n height:1.5em;\n width:1.5em;\n margin-right:0.5em;\n border:1px solid rgba(0,0,0,0.2);\n display: inline-block;\n vertical-align: middle;\n border-radius: 2px;\n}\n\n#${instanceId} .legend p {\n vertical-align: text-top;\n margin-bottom: 0.6em;\n}\n\n#${instanceId} .legend h4 {\n font-weight:600;\n font-size:1.1em;\n padding: 0;\n margin: 0 0.4em 0.4em 0;\n display: inline-block;\n}\n\n#${instanceId} .legend .minimize {\n float:right;\n}\n\n#${instanceId} .legend .minimize .circle-background {\n fill: rgba(128,128,128,0.3); cursor: pointer;\n}\n\n#${instanceId} .legend .minimize .circle-background:hover {\n fill: rgba(128,128,128,0.5);\n}\n\n#${instanceId} .legend-column {\n display: inline-block;\n vertical-align: top;\n margin-right: 10px;\n}\n\n#${instanceId} .legend-column:last-child {\n margin-right:0;\n}\n\n/* a 0-specificity class selector. Allows override for background elements by grafana's version of this class */\ndiv:where(.tight-form-func) { background: #FFF; }\na:where(.tight-form-func) { background: #FFF; }\n\n#${instanceId} .animated-node { \n transform: scale(1.5, 1.5);\n}\n\n#${instanceId} svg .control.control-selected { \n animation-name: pulse;\n animation-duration: 1s;\n animation-iteration-count: infinite;\n animation-timing-function: step-end;\n}\n\n@keyframes pulse {\n 0% { opacity:1.0 }\n 80% { opacity:0.0 }\n 100% { opacity:1.0 }\n}\n\n#${instanceId} .loading-overlay, .error-overlay { background-color:rgba(0,0,0,0.7); position:absolute; height:100%; width: 100%; color:white; font-weight: bold; justify-content: center; align-items: center; z-index:20000; }\n\n";
export const esmapCss: "\n#${instanceId} .leaflet-pane svg path.edge {\n pointer-events: stroke;\n}\n#${instanceId} .leaflet-pane svg path.control {\n pointer-events: stroke;\n}\n\n#${instanceId} .map-panel {\n position: relative;\n}\n\n#${instanceId} svg path.edge-az {\n marker-start: url(\"#arrow\");\n}\n\n#${instanceId} svg path.edge-za {\n marker-start: url(\"#arrow\");\n}\n\n#${instanceId} svg circle.node {\n /* fill: #999; */\n stroke: #777;\n stroke-width: 1;\n pointer-events: all;\n}\n\n#${instanceId} svg path.edge {\n stroke-linecap: butt;\n fill: none;\n pointer-events: visiblePainted !important;\n}\n\n#${instanceId} svg path.animated-edge.edge-az {\n stroke: transparent;\n stroke-linecap: butt;\n fill: none;\n cursor: crosshair;\n}\n\n#${instanceId} svg path.animated-edge.edge-za {\n stroke: transparent;\n stroke-linecap: butt;\n fill: none;\n cursor: crosshair;\n}\n\n#${instanceId} svg path.animated-edge.edge-az.selected {\n}\n\n#${instanceId} svg path.animated-edge.edge-za.selected {\n}\n\n@keyframes dash {\n to {\n stroke-dashoffset: 0;\n }\n}\n\n\n#${instanceId} svg path.control {\n stroke-dasharray: 8 1;\n stroke-width: 6;\n stroke: #f808;\n fill: none;\n cursor: crosshair;\n}\n\n#${instanceId} svg circle.controlPoint {\n stroke: black;\n stroke-width: 1;\n fill: #f80;\n cursor: move;\n}\n\n#${instanceId} div.tooltip-hover {\n position:absolute;\n border-radius:4px;\n padding:10px;\n margin:10px;\n max-width:250px;\n box-shadow: 3px 3px 4px rgba(0, 0, 0, 0.2);\n font-family: sans-serif;\n z-index: 300;\n}\n\n#${instanceId} div.tooltip-hover svg {\n height: 12px;\n width: 12px;\n stroke-width: 2px;\n}\n\n#${instanceId} div.tooltip-hover p:first-of-type {\n margin-top:0;\n}\n\n#${instanceId} div.tooltip-hover p {\n margin-top: 6px;\n margin-bottom:0;\n}\n\n#${instanceId} .legend-text {\n padding-left: 5px;\n vertical-align: middle;\n}\n\n\n\n#${instanceId} .home-overlay {\n position: absolute;\n margin-top: 12px;\n margin-left: 57px;\n}\n#${instanceId} .home-overlay > .button {\n border-radius: 4px;\n padding: 5px 10px;\n margin-right: 5px;\n box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.5);\n display: inline-block;\n cursor: pointer;\n}\n\n#${instanceId} .legend {\n position: absolute;\n padding: 1em 1em 0.3em 1em;\n margin: 0.8em;\n box-shadow: 0px 0px 2px rgb(0 0 0 / 50%);\n border-radius: 3px;\n}\n\n#${instanceId} .legend.topright {\n top: 0;\n right: 0;\n}\n\n#${instanceId} .legend.bottomright {\n bottom: 0;\n right: 0;\n margin: 0.8em 0.8em 1.8em 0.8em;\n}\n\n#${instanceId} .legend.bottomleft {\n bottom: 0;\n left: 0;\n}\n\n#${instanceId} .color-sample {\n height:1.5em;\n width:1.5em;\n margin-right:0.5em;\n border:1px solid rgba(0,0,0,0.2);\n display: inline-block;\n vertical-align: middle;\n border-radius: 2px;\n}\n\n#${instanceId} .legend p {\n vertical-align: text-top;\n margin-bottom: 0.6em;\n}\n\n#${instanceId} .legend h4 {\n font-weight:600;\n font-size:1.1em;\n padding: 0;\n margin: 0 0.4em 0.4em 0;\n display: inline-block;\n}\n\n#${instanceId} .legend .minimize {\n float:right;\n}\n\n#${instanceId} .legend .minimize .circle-background {\n fill: rgba(128,128,128,0.3); cursor: pointer;\n}\n\n#${instanceId} .legend .minimize .circle-background:hover {\n fill: rgba(128,128,128,0.5);\n}\n\n#${instanceId} .legend-column {\n display: inline-block;\n vertical-align: top;\n margin-right: 10px;\n}\n\n#${instanceId} .legend-column:last-child {\n margin-right:0;\n}\n\n/* a 0-specificity class selector. Allows override for background elements by grafana's version of this class */\ndiv:where(.tight-form-func) { background: #FFF; }\na:where(.tight-form-func) { background: #FFF; }\n\n#${instanceId} .animated-node { \n transform: scale(1.5, 1.5);\n}\n\n#${instanceId} svg .control.control-selected { \n animation-name: pulse;\n animation-duration: 1s;\n animation-iteration-count: infinite;\n animation-timing-function: step-end;\n}\n\n@keyframes pulse {\n 0% { opacity:1.0 }\n 80% { opacity:0.0 }\n 100% { opacity:1.0 }\n}\n\n#${instanceId} .loading-overlay, .error-overlay { background-color:rgba(0,0,0,0.7); position:absolute; height:100%; width: 100%; color:white; font-weight: bold; justify-content: center; align-items: center; z-index:20000; }\n\n";
//# sourceMappingURL=esmap.css.d.ts.map
2 changes: 1 addition & 1 deletion dist/components/css/esmap.css.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions dist/components/hoc/withThemeWrapper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';
/**
* High order component HOC for wrapping useTheme2 hook to enable use with React class components.
* @param {Component} Component
* @returns {Component} The same component, wrapped with the useTheme2 hook from Grafana.
*/
export declare function withTheme(Component: any): (props: any) => React.JSX.Element;
//# sourceMappingURL=withThemeWrapper.d.ts.map
1 change: 1 addition & 0 deletions dist/components/hoc/withThemeWrapper.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/module.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/module.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
}
],
"version": "3.0.1",
"updated": "2024-09-17"
"updated": "2024-10-03"
},
"dependencies": {
"grafanaDependency": ">=9.0.0",
Expand Down
4 changes: 2 additions & 2 deletions docs/development.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

## Development Notes

This project was built in Node 18.20.4 (LTS Hydrogen) and must built using Yarn (1.22.22 or higher).
This project was built in Node 18.20.4 (LTS Hydrogen) and must be built using Yarn (1.22.22 or higher).

## Table of Contents

Expand Down Expand Up @@ -65,7 +65,7 @@ Also, only component testing will be run. Integration E2E tests must be run sepa
5. Install Playwright browsers for testing (this only needs to be done once).

```sh
$ npx playwright install # only needs to be done once -or- when an upgrade in browsers is desired
$ npx playwright install # only needs to be done once -or- when an upgrade in browsers is desired/required
```

6. Build the project using `make prod` (`prod` is not a typo). A failure during signing is expected for local development.
Expand Down
19 changes: 0 additions & 19 deletions e2e/interfaces/Panel.inteface.ts

This file was deleted.

11 changes: 0 additions & 11 deletions e2e/interfaces/Targets.interface.ts

This file was deleted.

87 changes: 87 additions & 0 deletions e2e/plugin.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,98 @@ import * as pubsub from '../src/components/lib/pubsub';
import { IFlowSheet } from '../src/types';
import { getEditNetworkMapPanelUrl, getHomepageUrl } from './util/urlHelpers';
import { setupFixtures } from './util/fixtures';
import { BasicColorMatcher } from './matchers/BasicColorMatcher';
import { IThreshold } from './interfaces/Threshold.interface';

const PubSub = pubsub.PubSub;

const PLUGIN_TEST_TIMEOUT = 120000; // 120s for plugin test

/**
* Checks the color of strokes for a given array of edge names.
* */
const getCheckStrokeColorFn = (page: Page) => {
return async (edgeNames: string[], colorNameMatcher?: RegExp | string) => {
// actual paths in gray by default
let edgeCount = edgeNames.length;
for (const testEdge of edgeNames) {
console.log(`Test Edge: ${testEdge}`);
const edgeLocator: Locator = await page.locator(`.topology > path[class*="edge-az-${testEdge}"]`);
if ((await edgeLocator.all()).length > 0) {
await expect(edgeLocator).toHaveCSS('stroke', colorNameMatcher ?? BasicColorMatcher.GREY);
}
}

// control paths in orange (always orange, but may not visible depending on enable/disable edge edit mode)
for (let cpIndex = 0; cpIndex < edgeCount; cpIndex++) {
const controlPathLocator: Locator = await page.locator(`.cp > path[data-index="${cpIndex}"]`);
if (await controlPathLocator.isVisible()) {
await expect(controlPathLocator).toHaveCSS('stroke', BasicColorMatcher.ORANGE);
}
}
};
};

const getThresholds = async (inFs: IFlowSheet, page: Page): Promise<IThreshold[]> => {
// only need the first swatch for the high value
let baseThresholdLoc: Locator = page.locator('#Thresholds [class*="inputWrapper"]').getByRole('button').first();
const baseLabelAttr = await baseThresholdLoc.getAttribute('aria-label');
expect(baseLabelAttr).toMatch(/color/);

const thresholdsLoc = page.locator('#Thresholds');

// all remaining swatches work their way from high to base
const colorSwatches: Locator[] = await thresholdsLoc.getByRole('button').all();
colorSwatches.shift();
let thresholdsRemaining = colorSwatches.length;
let thresholdsAppended = 0;

const resultThresholds: IThreshold[] = [];

while (thresholdsRemaining > 0) {
const colorSwatchLoc: Locator = colorSwatches.pop()!;

// derive color from class
const swatchAttr = colorSwatchLoc.getAttribute ? await colorSwatchLoc.getAttribute('aria-label') : null;
const attrVals = swatchAttr ? swatchAttr.split(' ') : null;
if (!attrVals || attrVals.find(val => /Remove/.test(val))) {
thresholdsRemaining--;
continue;
}
const swatchColor = attrVals.filter(s => s !== 'color').join(' ');

// derive flow threshold level from order
let expectedFlow;
if (thresholdsRemaining === 1) {
expectedFlow = 'high';
} else if (thresholdsRemaining > 1 && thresholdsAppended > 0) {
expectedFlow = `base+${thresholdsRemaining}`;
} else {
expectedFlow = 'base';
}

// derive meta data from flowsheet
const endpoint = inFs.name;

// append to thresholds
const currentThreshold: IThreshold = {
locator: colorSwatchLoc,
expectedFlow,
swatchColor,
meta: {
type: 'edge',
endpoints: [endpoint]
}
};
resultThresholds.push(currentThreshold);
thresholdsAppended++;
thresholdsRemaining--;
}

return resultThresholds;
};


pluginTest.describe("plugin testing", () => {
pluginTest.describe.configure({ mode: "serial" });
pluginTest.setTimeout(PLUGIN_TEST_TIMEOUT);
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "esnet-networkmap-panel",
"version": "3.0.1",
"version": "3.0.2",
"description": "Network Map Panel",
"repository": "https://github.com/esnet/grafana-esnet-networkmap-panel",
"scripts": {
Expand Down Expand Up @@ -84,7 +84,7 @@
"main": "index.js",
"types": "dist/exports.d.ts",
"engines": {
"node": ">=15"
"node": ">=16"
},
"dependencies": {
"@emotion/css": "11.10.6",
Expand Down
11 changes: 8 additions & 3 deletions src/MapPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import React, { Component } from 'react';
import { PanelProps, createTheme, getValueFormat, DataFrameView, sortDataFrame, getTimeField, EventBus } from '@grafana/data';
import { PanelProps, createTheme, getValueFormat, DataFrameView, sortDataFrame, getTimeField, EventBus, GrafanaTheme2 } from '@grafana/data';
import { MapOptions } from './types';
import { sanitizeTopology } from './components/lib/topologyTools';
import './components/MapCanvas.component.js';
import { PubSub } from './components/lib/pubsub.js';
import { locationService } from '@grafana/runtime';
import { LAYER_LIMIT, setPath } from "./components/lib/utils.js"
import { signals } from "./signals.js"
import { withTheme } from "./components/hoc/withThemeWrapper";

export interface MapPanelProps extends PanelProps<MapOptions> {
interface MapPanelProps extends PanelProps<MapOptions> {
fieldConfig: any;
options: MapOptions;
eventBus: EventBus;
theme: GrafanaTheme2;
}

export function toDataFrames(data){
Expand All @@ -28,7 +30,7 @@ export function toDataFrames(data){
return dataFrames;
}

export class MapPanel extends Component<MapPanelProps> {
class MapPanel extends Component<MapPanelProps> {
mapCanvas: any;
lastOptions: any;
lastTopology: any;
Expand Down Expand Up @@ -413,3 +415,6 @@ export class MapPanel extends Component<MapPanelProps> {
return elem;
}
}

const mapPanelWithTheme = withTheme(MapPanel);
export { mapPanelWithTheme as MapPanel, MapPanelProps };
Loading

0 comments on commit ccffeb1

Please sign in to comment.