From 9bb49204979a7d873214659e92c4eaf7ac662a82 Mon Sep 17 00:00:00 2001
From: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com>
Date: Thu, 24 Feb 2022 14:04:40 -0600
Subject: [PATCH] UI/client count tests (#14162)
---
ui/README.md | 6 +
ui/app/components/clients/attribution.js | 46 +--
ui/app/components/clients/current.js | 6 +-
ui/app/components/clients/history.js | 16 +-
.../clients/horizontal-bar-chart.js | 4 +-
ui/app/components/clients/line-chart.js | 41 ++-
.../components/clients/vertical-bar-chart.js | 6 +-
ui/app/components/date-dropdown.js | 2 +-
.../routes/vault/cluster/clients/history.js | 16 +-
ui/app/styles/core/charts.scss | 15 +-
.../components/clients/attribution.hbs | 72 +++--
.../templates/components/clients/current.hbs | 4 +-
.../templates/components/clients/history.hbs | 9 +-
.../components/clients/line-chart.hbs | 7 +-
.../components/clients/usage-stats.hbs | 3 +
.../components/clients/vertical-bar-chart.hbs | 7 +-
ui/app/templates/components/date-dropdown.hbs | 9 +-
ui/app/utils/chart-helpers.js | 4 +-
.../addon/templates/components/stat-text.hbs | 6 +-
.../addon/templates/components/toolbar.hbs | 2 +-
ui/mirage/handlers/activity.js | 8 +-
ui/mirage/handlers/clients.js | 12 +-
ui/tests/acceptance/client-current-test.js | 172 +++++++++++
ui/tests/acceptance/client-history-test.js | 278 ++++++++++++++++++
ui/tests/acceptance/oidc-provider-test.js | 16 +-
ui/tests/helpers/clients.js | 183 ++++++++++++
.../components/clients-current-test.js | 69 -----
.../components/clients-history-test.js | 103 -------
.../components/clients/attribution-test.js | 144 +++++++++
.../config-test.js} | 0
.../clients/horizontal-bar-chart-test.js | 85 ++++++
.../components/clients/line-chart-test.js | 40 +++
.../components/clients/usage-stats-test.js | 46 +++
.../clients/vertical-bar-chart-test.js | 30 ++
.../components/date-dropdown-test.js | 174 +++++++++++
ui/tests/unit/utils/chart-helpers-test.js | 30 ++
36 files changed, 1369 insertions(+), 302 deletions(-)
create mode 100644 ui/tests/acceptance/client-current-test.js
create mode 100644 ui/tests/acceptance/client-history-test.js
create mode 100644 ui/tests/helpers/clients.js
delete mode 100644 ui/tests/integration/components/clients-current-test.js
delete mode 100644 ui/tests/integration/components/clients-history-test.js
create mode 100644 ui/tests/integration/components/clients/attribution-test.js
rename ui/tests/integration/components/{clients-config-test.js => clients/config-test.js} (100%)
create mode 100644 ui/tests/integration/components/clients/horizontal-bar-chart-test.js
create mode 100644 ui/tests/integration/components/clients/line-chart-test.js
create mode 100644 ui/tests/integration/components/clients/usage-stats-test.js
create mode 100644 ui/tests/integration/components/clients/vertical-bar-chart-test.js
create mode 100644 ui/tests/integration/components/date-dropdown-test.js
create mode 100644 ui/tests/unit/utils/chart-helpers-test.js
diff --git a/ui/README.md b/ui/README.md
index d8b8a72aa..62a7983bb 100644
--- a/ui/README.md
+++ b/ui/README.md
@@ -78,6 +78,12 @@ long-form version of the npm script:
`ember server --proxy=http://localhost:PORT`
+To run yarn with mirage, do:
+
+- `yarn start:mirage handlername`
+
+Where `handlername` is one of the options exported in `mirage/handlers/index`
+
### Code Generators
Make use of the many generators for code, try `ember help generate` for more details. If you're using a component that can be widely-used, consider making it an `addon` component instead (see [this PR](https://github.com/hashicorp/vault/pull/6629) for more details)
diff --git a/ui/app/components/clients/attribution.js b/ui/app/components/clients/attribution.js
index 75bcde84d..edafc1001 100644
--- a/ui/app/components/clients/attribution.js
+++ b/ui/app/components/clients/attribution.js
@@ -39,6 +39,9 @@ export default class Attribution extends Component {
}
get isSingleNamespace() {
+ if (!this.args.totalClientsData) {
+ return 'no data';
+ }
// if a namespace is selected, then we're viewing top 10 auth methods (mounts)
return !!this.args.selectedNamespace;
}
@@ -61,29 +64,30 @@ export default class Attribution extends Component {
get chartText() {
let dateText = this.isDateRange ? 'date range' : 'month';
- if (!this.isSingleNamespace) {
- return {
- description:
- 'This data shows the top ten namespaces by client count and can be used to understand where clients are originating. Namespaces are identified by path. To see all namespaces, export this data.',
- newCopy: `The new clients in the namespace for this ${dateText}.
+ switch (this.isSingleNamespace) {
+ case true:
+ return {
+ description:
+ 'This data shows the top ten authentication methods by client count within this namespace, and can be used to understand where clients are originating. Authentication methods are organized by path.',
+ newCopy: `The new clients used by the auth method for this ${dateText}. This aids in understanding which auth methods create and use new clients
+ ${dateText === 'date range' ? ' over time.' : '.'}`,
+ totalCopy: `The total clients used by the auth method for this ${dateText}. This number is useful for identifying overall usage volume. `,
+ };
+ case false:
+ return {
+ description:
+ 'This data shows the top ten namespaces by client count and can be used to understand where clients are originating. Namespaces are identified by path. To see all namespaces, export this data.',
+ newCopy: `The new clients in the namespace for this ${dateText}.
This aids in understanding which namespaces create and use new clients
${dateText === 'date range' ? ' over time.' : '.'}`,
- totalCopy: `The total clients in the namespace for this ${dateText}. This number is useful for identifying overall usage volume.`,
- };
- } else if (this.isSingleNamespace) {
- return {
- description:
- 'This data shows the top ten authentication methods by client count within this namespace, and can be used to understand where clients are originating. Authentication methods are organized by path.',
- newCopy: `The new clients used by the auth method for this ${dateText}. This aids in understanding which auth methods create and use new clients
- ${dateText === 'date range' ? ' over time.' : '.'}`,
- totalCopy: `The total clients used by the auth method for this ${dateText}. This number is useful for identifying overall usage volume. `,
- };
- } else {
- return {
- description: 'There is a problem gathering data',
- newCopy: 'There is a problem gathering data',
- totalCopy: 'There is a problem gathering data',
- };
+ totalCopy: `The total clients in the namespace for this ${dateText}. This number is useful for identifying overall usage volume.`,
+ };
+ case 'no data':
+ return {
+ description: 'There is a problem gathering data',
+ };
+ default:
+ return '';
}
}
diff --git a/ui/app/components/clients/current.js b/ui/app/components/clients/current.js
index 1e8a49080..3f00c3945 100644
--- a/ui/app/components/clients/current.js
+++ b/ui/app/components/clients/current.js
@@ -29,7 +29,11 @@ export default class Current extends Component {
}
get hasAttributionData() {
- return this.totalUsageCounts.clients !== 0 && !!this.totalClientsData && !this.selectedAuthMethod;
+ if (this.selectedAuthMethod) return false;
+ if (this.selectedNamespace) {
+ return this.authMethodOptions.length > 0;
+ }
+ return this.totalUsageCounts.clients !== 0 && !!this.totalClientsData;
}
get filteredActivity() {
diff --git a/ui/app/components/clients/history.js b/ui/app/components/clients/history.js
index fdd163956..07a59ff10 100644
--- a/ui/app/components/clients/history.js
+++ b/ui/app/components/clients/history.js
@@ -62,10 +62,12 @@ export default class History extends Component {
// SEARCH SELECT
@tracked selectedNamespace = null;
- @tracked namespaceArray = this.getActivityResponse.byNamespace.map((namespace) => ({
- name: namespace.label,
- id: namespace.label,
- }));
+ @tracked namespaceArray = this.getActivityResponse.byNamespace
+ ? this.getActivityResponse.byNamespace.map((namespace) => ({
+ name: namespace.label,
+ id: namespace.label,
+ }))
+ : [];
// TEMPLATE MESSAGING
@tracked noActivityDate = '';
@@ -102,7 +104,11 @@ export default class History extends Component {
}
get hasAttributionData() {
- return this.totalUsageCounts.clients !== 0 && !!this.totalClientsData && !this.selectedAuthMethod;
+ if (this.selectedAuthMethod) return false;
+ if (this.selectedNamespace) {
+ return this.authMethodOptions.length > 0;
+ }
+ return !!this.totalClientsData && this.totalUsageCounts && this.totalUsageCounts.clients !== 0;
}
get startTimeDisplay() {
diff --git a/ui/app/components/clients/horizontal-bar-chart.js b/ui/app/components/clients/horizontal-bar-chart.js
index f387c0ea1..429465357 100644
--- a/ui/app/components/clients/horizontal-bar-chart.js
+++ b/ui/app/components/clients/horizontal-bar-chart.js
@@ -21,8 +21,6 @@ import { tracked } from '@glimmer/tracking';
* @param {array} chartLegend - array of objects with key names 'key' and 'label' so data can be stacked
*/
-// TODO CMB: delete original bar chart component
-
// SIZING CONSTANTS
const CHART_MARGIN = { top: 10, left: 95 }; // makes space for y-axis legend
const TRANSLATE = { down: 14, left: 99 };
@@ -104,7 +102,7 @@ export default class HorizontalBarChart extends Component {
.append('rect')
.attr('class', 'data-bar')
.style('cursor', 'pointer')
- .attr('width', (chartData) => `${xScale(chartData[1] - chartData[0])}%`)
+ .attr('width', (chartData) => `${xScale(Math.abs(chartData[1] - chartData[0]))}%`)
.attr('height', yScale.bandwidth())
.attr('x', (chartData) => `${xScale(chartData[0])}%`)
.attr('y', ({ data }) => yScale(data[labelKey]))
diff --git a/ui/app/components/clients/line-chart.js b/ui/app/components/clients/line-chart.js
index 64c55ef0f..2984a7606 100644
--- a/ui/app/components/clients/line-chart.js
+++ b/ui/app/components/clients/line-chart.js
@@ -27,6 +27,14 @@ export default class LineChart extends Component {
@tracked tooltipTotal = '';
@tracked tooltipNew = '';
+ get yKey() {
+ return this.args.yKey || 'clients';
+ }
+
+ get xKey() {
+ return this.args.xKey || 'month';
+ }
+
@action removeTooltip() {
this.tooltipTarget = null;
}
@@ -39,15 +47,17 @@ export default class LineChart extends Component {
// DEFINE AXES SCALES
let yScale = scaleLinear()
- .domain([0, max(dataset.map((d) => d.clients))])
- .range([0, 100]);
+ .domain([0, max(dataset.map((d) => d[this.yKey]))])
+ .range([0, 100])
+ .nice();
let yAxisScale = scaleLinear()
- .domain([0, max(dataset.map((d) => d.clients))])
- .range([SVG_DIMENSIONS.height, 0]);
+ .domain([0, max(dataset.map((d) => d[this.yKey]))])
+ .range([SVG_DIMENSIONS.height, 0])
+ .nice();
let xScale = scalePoint() // use scaleTime()?
- .domain(dataset.map((d) => d.month))
+ .domain(dataset.map((d) => d[this.xKey]))
.range([0, SVG_DIMENSIONS.width])
.padding(0.2);
@@ -67,8 +77,8 @@ export default class LineChart extends Component {
// PATH BETWEEN PLOT POINTS
let lineGenerator = line()
- .x((d) => xScale(d.month))
- .y((d) => yAxisScale(d.clients));
+ .x((d) => xScale(d[this.xKey]))
+ .y((d) => yAxisScale(d[this.yKey]));
chartSvg
.append('g')
@@ -86,8 +96,8 @@ export default class LineChart extends Component {
.enter()
.append('circle')
.attr('class', 'data-plot')
- .attr('cy', (d) => `${100 - yScale(d.clients)}%`)
- .attr('cx', (d) => xScale(d.month))
+ .attr('cy', (d) => `${100 - yScale(d[this.yKey])}%`)
+ .attr('cx', (d) => xScale(d[this.xKey]))
.attr('r', 3.5)
.attr('fill', LIGHT_AND_DARK_BLUE[0])
.attr('stroke', LIGHT_AND_DARK_BLUE[1])
@@ -103,18 +113,19 @@ export default class LineChart extends Component {
.attr('class', 'hover-circle')
.style('cursor', 'pointer')
.style('opacity', '0')
- .attr('cy', (d) => `${100 - yScale(d.clients)}%`)
- .attr('cx', (d) => xScale(d.month))
+ .attr('cy', (d) => `${100 - yScale(d[this.yKey])}%`)
+ .attr('cx', (d) => xScale(d[this.xKey]))
.attr('r', 10);
let hoverCircles = chartSvg.selectAll('.hover-circle');
// MOUSE EVENT FOR TOOLTIP
hoverCircles.on('mouseover', (data) => {
- this.tooltipMonth = data.month;
- this.tooltipTotal = `${data.clients} total clients`;
- this.tooltipNew = `${data.new_clients.clients} new clients`;
- let node = hoverCircles.filter((plot) => plot.month === data.month).node();
+ // TODO: how to genericize this?
+ this.tooltipMonth = data[this.xKey];
+ this.tooltipTotal = `${data[this.yKey]} total clients`;
+ this.tooltipNew = `${data.new_clients?.clients} new clients`;
+ let node = hoverCircles.filter((plot) => plot[this.xKey] === data[this.xKey]).node();
this.tooltipTarget = node;
});
}
diff --git a/ui/app/components/clients/vertical-bar-chart.js b/ui/app/components/clients/vertical-bar-chart.js
index c77214622..f6676f2bd 100644
--- a/ui/app/components/clients/vertical-bar-chart.js
+++ b/ui/app/components/clients/vertical-bar-chart.js
@@ -47,7 +47,7 @@ export default class VerticalBarChart extends Component {
// DEFINE DATA BAR SCALES
let yScale = scaleLinear()
- .domain([0, max(dataset.map((d) => d.total))]) // TODO will need to recalculate when you get the data
+ .domain([0, max(dataset.map((d) => d.clients))]) // TODO will need to recalculate when you get the data
.range([0, 100])
.nice();
@@ -76,7 +76,7 @@ export default class VerticalBarChart extends Component {
// MAKE AXES //
let yAxisScale = scaleLinear()
- .domain([0, max(dataset.map((d) => d.total))]) // TODO will need to recalculate when you get the data
+ .domain([0, max(dataset.map((d) => d.clients))]) // TODO will need to recalculate when you get the data
.range([`${SVG_DIMENSIONS.height}`, 0])
.nice();
@@ -116,7 +116,7 @@ export default class VerticalBarChart extends Component {
// MOUSE EVENT FOR TOOLTIP
tooltipRect.on('mouseover', (data) => {
let hoveredMonth = data.month;
- this.tooltipTotal = `${data.total} ${data.new_clients ? 'total' : 'new'} clients`;
+ this.tooltipTotal = `${data.clients} ${data.new_clients ? 'total' : 'new'} clients`;
this.uniqueEntities = `${data.entity_clients} unique entities`;
this.nonEntityTokens = `${data.non_entity_clients} non-entity tokens`;
// let node = chartSvg
diff --git a/ui/app/components/date-dropdown.js b/ui/app/components/date-dropdown.js
index c40b412f2..bf9df3754 100644
--- a/ui/app/components/date-dropdown.js
+++ b/ui/app/components/date-dropdown.js
@@ -8,7 +8,7 @@ import { tracked } from '@glimmer/tracking';
*
* @example
* ```js
- *
{{this.chartText.description}}
+{{this.chartText.description}}
{{this.chartText.totalCopy}}
+{{this.topClientCounts.label}}
+{{this.chartText.totalCopy}}
-{{format-number this.topClientCounts.clients}}
+{{this.topClientCounts.label}}
-{{format-number this.topClientCounts.clients}}
-{{this.startTimeDisplay}}
+{{this.startTimeDisplay}}