diff --git a/web/ui/react-app/package.json b/web/ui/react-app/package.json index 71b9a45f90..4cd1304252 100644 --- a/web/ui/react-app/package.json +++ b/web/ui/react-app/package.json @@ -18,6 +18,7 @@ "@types/react-resize-detector": "^4.0.2", "@types/sanitize-html": "^1.20.2", "bootstrap": "^4.2.1", + "css.escape": "^1.5.1", "downshift": "^3.2.2", "enzyme-to-json": "^3.4.3", "fuzzy": "^0.1.3", diff --git a/web/ui/react-app/src/pages/targets/ScrapePoolList.test.tsx b/web/ui/react-app/src/pages/targets/ScrapePoolList.test.tsx index de705de95b..ad3d6c9fcf 100644 --- a/web/ui/react-app/src/pages/targets/ScrapePoolList.test.tsx +++ b/web/ui/react-app/src/pages/targets/ScrapePoolList.test.tsx @@ -10,7 +10,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSpinner } from '@fortawesome/free-solid-svg-icons'; import { FetchMock } from 'jest-fetch-mock/types'; -describe('Flags', () => { +describe('ScrapePoolList', () => { const defaultProps = { filter: { showHealthy: true, showUnhealthy: true }, pathPrefix: '..', @@ -39,7 +39,7 @@ describe('Flags', () => { let mock: FetchMock; beforeEach(() => { //Tooltip requires DOM elements to exist. They do not in enzyme rendering so we must manually create them. - const scrapePools: { [key: string]: number } = { blackbox: 3, node_exporter: 1, prometheus: 1 }; + const scrapePools: { [key: string]: number } = { blackbox: 3, node_exporter: 1, 'prometheus/test': 1 }; Object.keys(scrapePools).forEach((pool: string): void => { Array.from(Array(scrapePools[pool]).keys()).forEach((idx: number): void => { const div = document.createElement('div'); diff --git a/web/ui/react-app/src/pages/targets/TargetLabels.test.tsx b/web/ui/react-app/src/pages/targets/TargetLabels.test.tsx index 51f402c1f3..c25e95d8f1 100644 --- a/web/ui/react-app/src/pages/targets/TargetLabels.test.tsx +++ b/web/ui/react-app/src/pages/targets/TargetLabels.test.tsx @@ -25,7 +25,7 @@ describe('targetLabels', () => { it('renders a div of series labels', () => { const div = targetLabels.find('div').filterWhere(elem => elem.hasClass('series-labels-container')); expect(div).toHaveLength(1); - expect(div.prop('id')).toEqual('series-labels-cortex/node-exporter_group/0-1'); + expect(div.prop('id')).toEqual('series-labels-cortex\\/node-exporter_group\\/0-1'); }); it('wraps each label in a label badge', () => { @@ -41,7 +41,7 @@ describe('targetLabels', () => { const tooltip = targetLabels.find(Tooltip); expect(tooltip).toHaveLength(1); expect(tooltip.prop('isOpen')).toBe(false); - expect(tooltip.prop('target')).toEqual('series-labels-cortex/node-exporter_group/0-1'); + expect(tooltip.prop('target')).toEqual('series-labels-cortex\\/node-exporter_group\\/0-1'); }); it('renders discovered labels', () => { diff --git a/web/ui/react-app/src/pages/targets/TargetLabels.tsx b/web/ui/react-app/src/pages/targets/TargetLabels.tsx index 0e8dfeacec..4fffdcfb2c 100644 --- a/web/ui/react-app/src/pages/targets/TargetLabels.tsx +++ b/web/ui/react-app/src/pages/targets/TargetLabels.tsx @@ -1,5 +1,6 @@ import React, { FC, Fragment, useState } from 'react'; import { Badge, Tooltip } from 'reactstrap'; +import 'css.escape'; import styles from './TargetLabels.module.css'; interface Labels { @@ -19,7 +20,7 @@ const TargetLabels: FC = ({ discoveredLabels, labels, idx, sc const [tooltipOpen, setTooltipOpen] = useState(false); const toggle = (): void => setTooltipOpen(!tooltipOpen); - const id = `series-labels-${scrapePool}-${idx}`; + const id = `series-labels-${CSS.escape(scrapePool)}-${idx}`; return ( <> diff --git a/web/ui/react-app/src/pages/targets/__snapshots__/TargetLabels.test.tsx.snap b/web/ui/react-app/src/pages/targets/__snapshots__/TargetLabels.test.tsx.snap index 3630b4fc89..8135de44c7 100644 --- a/web/ui/react-app/src/pages/targets/__snapshots__/TargetLabels.test.tsx.snap +++ b/web/ui/react-app/src/pages/targets/__snapshots__/TargetLabels.test.tsx.snap @@ -4,7 +4,7 @@ exports[`targetLabels renders discovered labels 1`] = `
diff --git a/web/ui/react-app/src/pages/targets/__testdata__/testdata.ts b/web/ui/react-app/src/pages/targets/__testdata__/testdata.ts index 4925b28752..0a6ed47e86 100644 --- a/web/ui/react-app/src/pages/targets/__testdata__/testdata.ts +++ b/web/ui/react-app/src/pages/targets/__testdata__/testdata.ts @@ -197,13 +197,13 @@ export const sampleApiResponse = Object.freeze({ __address__: 'localhost:9090', __metrics_path__: '/metrics', __scheme__: 'http', - job: 'prometheus', + job: 'prometheus/test', }, labels: { instance: 'localhost:9090', - job: 'prometheus', + job: 'prometheus/test', }, - scrapePool: 'prometheus', + scrapePool: 'prometheus/test', scrapeUrl: 'http://localhost:9090/metrics', lastError: '', lastScrape: '2019-11-04T11:52:18.479731-07:00', diff --git a/web/ui/react-app/src/pages/targets/target.test.ts b/web/ui/react-app/src/pages/targets/target.test.ts index ca56979f8e..e17ab59e77 100644 --- a/web/ui/react-app/src/pages/targets/target.test.ts +++ b/web/ui/react-app/src/pages/targets/target.test.ts @@ -8,7 +8,7 @@ describe('groupTargets', () => { const targetGroups: ScrapePools = groupTargets(targets); it('groups a list of targets by scrape job', () => { - ['blackbox', 'prometheus', 'node_exporter'].forEach(scrapePool => { + ['blackbox', 'prometheus/test', 'node_exporter'].forEach(scrapePool => { expect(Object.keys(targetGroups)).toContain(scrapePool); }); Object.keys(targetGroups).forEach((scrapePool: string): void => { @@ -20,7 +20,7 @@ describe('groupTargets', () => { }); it('adds upCount during aggregation', () => { - const testCases: { [key: string]: number } = { blackbox: 3, prometheus: 1, node_exporter: 1 }; + const testCases: { [key: string]: number } = { blackbox: 3, 'prometheus/test': 1, node_exporter: 1 }; Object.keys(testCases).forEach((scrapePool: string): void => { expect(targetGroups[scrapePool].upCount).toEqual(testCases[scrapePool]); }); diff --git a/web/ui/react-app/yarn.lock b/web/ui/react-app/yarn.lock index 04bbdaccc3..f650f09a85 100644 --- a/web/ui/react-app/yarn.lock +++ b/web/ui/react-app/yarn.lock @@ -3357,6 +3357,11 @@ css-what@2.1, css-what@^2.1.2: resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== +css.escape@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" + integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s= + css@^2.0.0: version "2.2.4" resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" @@ -4718,11 +4723,6 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" integrity sha1-2uRqnXj74lKSJYzB54CkHZXAN4I= -flot@^3.2.13: - version "3.2.13" - resolved "https://registry.yarnpkg.com/flot/-/flot-3.2.13.tgz#f4457fd6042fe4ac4e4e124e7a7c7256e69f5362" - integrity sha512-ZJl8zazqgbn79YCdyzt9JV1r38Gk7Dkt+tBb5Kx1sMEDvLVz+ViwF/QTWKcYjyaPO+UW58FP+fFWZFp6dXeMAA== - flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"