UI/1.11 client count component tests (#15748)
* add line chart test * add empty state option to line chart * add empty state test * add tooltip coverage * add test files * add monthly usage tests * finish tests * tidying * address comments, add average test * finish tests broken from calendar
This commit is contained in:
parent
7f7ef1cfe9
commit
55dba55bbe
|
@ -8,6 +8,7 @@ import { axisLeft } from 'd3-axis';
|
|||
import { max, maxIndex } from 'd3-array';
|
||||
import { BAR_COLOR_HOVER, GREY, LIGHT_AND_DARK_BLUE, formatTooltipNumber } from 'vault/utils/chart-helpers';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { formatNumber } from 'core/helpers/format-number';
|
||||
|
||||
/**
|
||||
* @module HorizontalBarChart
|
||||
|
@ -22,6 +23,7 @@ import { tracked } from '@glimmer/tracking';
|
|||
* @param {string} labelKey - string of key name for label value in chart data
|
||||
* @param {string} xKey - string of key name for x value in chart data
|
||||
* @param {object} totalCounts - object to calculate percentage for tooltip
|
||||
* @param {string} [noDataMessage] - custom empty state message that displays when no dataset is passed to the chart
|
||||
*/
|
||||
|
||||
// SIZING CONSTANTS
|
||||
|
@ -247,7 +249,7 @@ export default class HorizontalBarChart extends Component {
|
|||
.data(dataset)
|
||||
.enter()
|
||||
.append('text')
|
||||
.text((d) => d[xKey])
|
||||
.text((d) => formatNumber([d[xKey]]))
|
||||
.attr('fill', '#000')
|
||||
.attr('class', 'total-value')
|
||||
.style('font-size', '.8rem')
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
formatNumbers,
|
||||
} from 'vault/utils/chart-helpers';
|
||||
import { parseAPITimestamp, formatChartDate } from 'core/utils/date-formatters';
|
||||
import { formatNumber } from 'core/helpers/format-number';
|
||||
|
||||
/**
|
||||
* @module LineChart
|
||||
|
@ -21,10 +22,12 @@ import { parseAPITimestamp, formatChartDate } from 'core/utils/date-formatters';
|
|||
*
|
||||
* @example
|
||||
* ```js
|
||||
* <LineChart @dataset={dataset} />
|
||||
* <LineChart @dataset={{dataset}} @upgradeData={{this.versionHistory}}/>
|
||||
* ```
|
||||
* @param {string} xKey - string denoting key for x-axis data (data[xKey]) of dataset
|
||||
* @param {string} yKey - string denoting key for y-axis data (data[yKey]) of dataset
|
||||
* @param {array} upgradeData - array of objects containing version history from the /version-history endpoint
|
||||
* @param {string} [noDataMessage] - custom empty state message that displays when no dataset is passed to the chart
|
||||
*/
|
||||
|
||||
export default class LineChart extends Component {
|
||||
|
@ -42,6 +45,27 @@ export default class LineChart extends Component {
|
|||
return this.args.xKey || 'month';
|
||||
}
|
||||
|
||||
get upgradeData() {
|
||||
const upgradeData = this.args.upgradeData;
|
||||
if (!upgradeData) return null;
|
||||
if (!Array.isArray(upgradeData)) {
|
||||
console.debug('upgradeData must be an array of objects containing upgrade history');
|
||||
return null;
|
||||
} else if (!Object.keys(upgradeData[0]).includes('timestampInstalled')) {
|
||||
console.debug(
|
||||
`upgrade must be an object with the following key names: ['id', 'previousVersion', 'timestampInstalled']`
|
||||
);
|
||||
return null;
|
||||
} else {
|
||||
return upgradeData?.map((versionData) => {
|
||||
return {
|
||||
[this.xKey]: parseAPITimestamp(versionData.timestampInstalled, 'M/yy'),
|
||||
...versionData,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@action removeTooltip() {
|
||||
this.tooltipTarget = null;
|
||||
}
|
||||
|
@ -49,17 +73,10 @@ export default class LineChart extends Component {
|
|||
@action
|
||||
renderChart(element, [chartData]) {
|
||||
const dataset = chartData;
|
||||
const upgradeData = [];
|
||||
if (this.args.upgradeData) {
|
||||
this.args.upgradeData.forEach((versionData) =>
|
||||
upgradeData.push({ month: parseAPITimestamp(versionData.timestampInstalled, 'M/yy'), ...versionData })
|
||||
);
|
||||
}
|
||||
const filteredData = dataset.filter((e) => Object.keys(e).includes(this.yKey)); // months with data will contain a 'clients' key (otherwise only a timestamp)
|
||||
const domainMax = max(filteredData.map((d) => d[this.yKey]));
|
||||
const chartSvg = select(element);
|
||||
chartSvg.attr('viewBox', `-50 20 600 ${SVG_DIMENSIONS.height}`); // set svg dimensions
|
||||
|
||||
// clear out DOM before appending anything
|
||||
chartSvg.selectAll('g').remove().exit().data(filteredData).enter();
|
||||
|
||||
|
@ -93,7 +110,9 @@ export default class LineChart extends Component {
|
|||
chartSvg.selectAll('.domain').remove();
|
||||
|
||||
const findUpgradeData = (datum) => {
|
||||
return upgradeData.find((upgrade) => upgrade[this.xKey] === datum[this.xKey]);
|
||||
return this.upgradeData
|
||||
? this.upgradeData.find((upgrade) => upgrade[this.xKey] === datum[this.xKey])
|
||||
: null;
|
||||
};
|
||||
|
||||
// VERSION UPGRADE INDICATOR
|
||||
|
@ -104,6 +123,7 @@ export default class LineChart extends Component {
|
|||
.enter()
|
||||
.append('circle')
|
||||
.attr('class', 'upgrade-circle')
|
||||
.attr('data-test-line-chart', (d) => `upgrade-${d[this.xKey]}`)
|
||||
.attr('fill', UPGRADE_WARNING)
|
||||
.style('opacity', (d) => (findUpgradeData(d) ? '1' : '0'))
|
||||
.attr('cy', (d) => `${100 - yScale(d[this.yKey])}%`)
|
||||
|
@ -158,8 +178,8 @@ export default class LineChart extends Component {
|
|||
hoverCircles.on('mouseover', (data) => {
|
||||
// TODO: how to generalize this?
|
||||
this.tooltipMonth = formatChartDate(data[this.xKey]);
|
||||
this.tooltipTotal = data[this.yKey] + ' total clients';
|
||||
this.tooltipNew = (data?.new_clients[this.yKey] || '0') + ' new clients';
|
||||
this.tooltipTotal = formatNumber([data[this.yKey]]) + ' total clients';
|
||||
this.tooltipNew = (formatNumber([data?.new_clients[this.yKey]]) || '0') + ' new clients';
|
||||
this.tooltipUpgradeText = '';
|
||||
let upgradeInfo = findUpgradeData(data);
|
||||
if (upgradeInfo) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Component from '@glimmer/component';
|
||||
import { calculateAverageClients } from 'vault/utils/chart-helpers';
|
||||
import { calculateAverage } from 'vault/utils/chart-helpers';
|
||||
|
||||
/**
|
||||
* @module MonthlyUsage
|
||||
|
@ -34,13 +34,13 @@ import { calculateAverageClients } from 'vault/utils/chart-helpers';
|
|||
*/
|
||||
export default class MonthlyUsage extends Component {
|
||||
get averageTotalClients() {
|
||||
return calculateAverageClients(this.args.verticalBarChartData, 'clients') || '0';
|
||||
return calculateAverage(this.args.verticalBarChartData, 'clients') || '0';
|
||||
}
|
||||
|
||||
get averageNewClients() {
|
||||
return (
|
||||
calculateAverageClients(
|
||||
this.args.verticalBarChartData.map((d) => d.new_clients),
|
||||
calculateAverage(
|
||||
this.args.verticalBarChartData?.map((d) => d.new_clients),
|
||||
'clients'
|
||||
) || '0'
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Component from '@glimmer/component';
|
||||
import { calculateAverageClients } from 'vault/utils/chart-helpers';
|
||||
import { calculateAverage } from 'vault/utils/chart-helpers';
|
||||
|
||||
/**
|
||||
* @module RunningTotal
|
||||
|
@ -45,14 +45,14 @@ export default class RunningTotal extends Component {
|
|||
get entityClientData() {
|
||||
return {
|
||||
runningTotal: this.args.runningTotals.entity_clients,
|
||||
averageNewClients: calculateAverageClients(this.args.barChartData, 'entity_clients') || '0',
|
||||
averageNewClients: calculateAverage(this.args.barChartData, 'entity_clients') || '0',
|
||||
};
|
||||
}
|
||||
|
||||
get nonEntityClientData() {
|
||||
return {
|
||||
runningTotal: this.args.runningTotals.non_entity_clients,
|
||||
averageNewClients: calculateAverageClients(this.args.barChartData, 'non_entity_clients') || '0',
|
||||
averageNewClients: calculateAverage(this.args.barChartData, 'non_entity_clients') || '0',
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -71,8 +71,8 @@ export default class RunningTotal extends Component {
|
|||
}
|
||||
|
||||
get showSingleMonth() {
|
||||
if (this.args.barChartData.length === 1) {
|
||||
const monthData = this.args.lineChartData[0];
|
||||
if (this.args.lineChartData?.length === 1) {
|
||||
const monthData = this.args?.lineChartData[0];
|
||||
return {
|
||||
total: {
|
||||
total: monthData.clients,
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
TRANSLATE,
|
||||
formatNumbers,
|
||||
} from 'vault/utils/chart-helpers';
|
||||
import { formatNumber } from 'core/helpers/format-number';
|
||||
|
||||
/**
|
||||
* @module VerticalBarChart
|
||||
|
@ -27,6 +28,7 @@ import {
|
|||
* @param {array} chartLegend - array of objects with key names 'key' and 'label' so data can be stacked
|
||||
* @param {string} xKey - string denoting key for x-axis data (data[xKey]) of dataset
|
||||
* @param {string} yKey - string denoting key for y-axis data (data[yKey]) of dataset
|
||||
* @param {string} [noDataMessage] - custom empty state message that displays when no dataset is passed to the chart
|
||||
*/
|
||||
|
||||
export default class VerticalBarChart extends Component {
|
||||
|
@ -83,6 +85,7 @@ export default class VerticalBarChart extends Component {
|
|||
.append('rect')
|
||||
.attr('width', '7px')
|
||||
.attr('class', 'data-bar')
|
||||
.attr('data-test-vertical-chart', 'data-bar')
|
||||
.attr('height', (stackedData) => `${yScale(stackedData[1] - stackedData[0])}%`)
|
||||
.attr('x', ({ data }) => xScale(data[this.xKey])) // uses destructuring because was data.data.month
|
||||
.attr('y', (data) => `${100 - yScale(data[1])}%`); // subtract higher than 100% to give space for x axis ticks
|
||||
|
@ -101,8 +104,13 @@ export default class VerticalBarChart extends Component {
|
|||
|
||||
const xAxis = axisBottom(xScale).tickSize(0);
|
||||
|
||||
yAxis(chartSvg.append('g'));
|
||||
xAxis(chartSvg.append('g').attr('transform', `translate(0, ${SVG_DIMENSIONS.height + 10})`));
|
||||
yAxis(chartSvg.append('g').attr('data-test-vertical-chart', 'y-axis-labels'));
|
||||
xAxis(
|
||||
chartSvg
|
||||
.append('g')
|
||||
.attr('transform', `translate(0, ${SVG_DIMENSIONS.height + 10})`)
|
||||
.attr('data-test-vertical-chart', 'x-axis-labels')
|
||||
);
|
||||
|
||||
chartSvg.selectAll('.domain').remove(); // remove domain lines
|
||||
|
||||
|
@ -129,9 +137,9 @@ export default class VerticalBarChart extends Component {
|
|||
// MOUSE EVENT FOR TOOLTIP
|
||||
tooltipRect.on('mouseover', (data) => {
|
||||
let hoveredMonth = data[this.xKey];
|
||||
this.tooltipTotal = `${data[this.yKey]} ${data.new_clients ? 'total' : 'new'} clients`;
|
||||
this.entityClients = `${data.entity_clients} entity clients`;
|
||||
this.nonEntityClients = `${data.non_entity_clients} non-entity clients`;
|
||||
this.tooltipTotal = `${formatNumber([data[this.yKey]])} ${data.new_clients ? 'total' : 'new'} clients`;
|
||||
this.entityClients = `${formatNumber([data.entity_clients])} entity clients`;
|
||||
this.nonEntityClients = `${formatNumber([data.non_entity_clients])} non-entity clients`;
|
||||
let node = chartSvg
|
||||
.selectAll('rect.data-bar')
|
||||
// filter for the top data bar (so y-coord !== 0) with matching month
|
||||
|
|
|
@ -130,10 +130,17 @@
|
|||
max-width: none;
|
||||
padding-right: 20px;
|
||||
padding-left: 20px;
|
||||
display: flex;
|
||||
|
||||
> div {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
> div.empty-state {
|
||||
white-space: nowrap;
|
||||
align-self: stretch;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-subTitle {
|
||||
|
|
|
@ -24,7 +24,10 @@
|
|||
|
||||
{{#if this.barChartTotalClients}}
|
||||
{{#if (or @isDateRange @isCurrentMonth)}}
|
||||
<div class="chart-container-wide" data-test-chart-container="total-clients">
|
||||
<div
|
||||
class={{concat (unless this.barChartTotalClients "chart-empty-state ") "chart-container-wide"}}
|
||||
data-test-chart-container="total-clients"
|
||||
>
|
||||
<Clients::HorizontalBarChart
|
||||
@dataset={{this.barChartTotalClients}}
|
||||
@chartLegend={{@chartLegend}}
|
||||
|
|
|
@ -204,6 +204,7 @@
|
|||
<button
|
||||
type="button"
|
||||
class="button link"
|
||||
data-test-date-modal-month={{month}}
|
||||
disabled={{if (lt index this.allowedMonthMax) false true}}
|
||||
{{on "click" (fn this.selectStartMonth month D.actions)}}
|
||||
>
|
||||
|
@ -230,6 +231,7 @@
|
|||
<button
|
||||
type="button"
|
||||
class="button link"
|
||||
data-test-date-modal-year={{year}}
|
||||
disabled={{if (eq year this.disabledYear) true false}}
|
||||
{{on "click" (fn this.selectStartYear year D.actions)}}
|
||||
>
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
>
|
||||
</svg>
|
||||
{{else}}
|
||||
<div class="chart-empty-state">
|
||||
<EmptyState @subTitle={{or @noDataMessage "No data to display"}} @bottomBorder={{true}} />
|
||||
</div>
|
||||
<EmptyState @subTitle={{or @noDataMessage "No data to display"}} @bottomBorder={{true}} />
|
||||
{{/if}}
|
||||
{{#if this.tooltipTarget}}
|
||||
{{! Required to set tag name = div https://github.com/yapplabs/ember-modal-dialog/issues/290 }}
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
<svg
|
||||
data-test-line-chart
|
||||
class="chart has-grid"
|
||||
{{on "mouseleave" this.removeTooltip}}
|
||||
{{did-insert this.renderChart @dataset}}
|
||||
{{did-update this.renderChart @dataset}}
|
||||
>
|
||||
</svg>
|
||||
{{#if @dataset}}
|
||||
<svg
|
||||
data-test-line-chart
|
||||
class="chart has-grid"
|
||||
{{on "mouseleave" this.removeTooltip}}
|
||||
{{did-insert this.renderChart @dataset}}
|
||||
{{did-update this.renderChart @dataset}}
|
||||
>
|
||||
</svg>
|
||||
{{else}}
|
||||
<EmptyState @subTitle={{or @noDataMessage "No data to display"}} @bottomBorder={{true}} />
|
||||
{{/if}}
|
||||
|
||||
{{! TOOLTIP }}
|
||||
|
||||
|
|
Before Width: | Height: | Size: 988 B After Width: | Height: | Size: 1.1 KiB |
|
@ -6,7 +6,7 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div class="chart-container-wide">
|
||||
<div class={{concat (unless @verticalBarChartData "chart-empty-state ") "chart-container-wide"}}>
|
||||
<Clients::VerticalBarChart @dataset={{@verticalBarChartData}} @chartLegend={{@chartLegend}} />
|
||||
</div>
|
||||
|
||||
|
@ -17,30 +17,31 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div class="data-details-top">
|
||||
<div class="data-details-top" data-test-monthly-usage-average-total>
|
||||
<h3 class="data-details">Average total clients per month</h3>
|
||||
<p class="data-details">
|
||||
{{format-number this.averageTotalClients}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="data-details-bottom">
|
||||
<div class="data-details-bottom" data-test-monthly-usage-average-new>
|
||||
<h3 class="data-details">Average new clients per month</h3>
|
||||
<p class="data-details">
|
||||
{{format-number this.averageNewClients}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="timestamp">
|
||||
<div data-test-monthly-usage-timestamp class="timestamp">
|
||||
{{#if @timestamp}}
|
||||
Updated
|
||||
{{date-format @timestamp "MMM d yyyy, h:mm:ss aaa"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="legend-right">
|
||||
<span class="light-dot"></span><span class="legend-label">{{capitalize @chartLegend.0.label}}</span>
|
||||
<span class="dark-dot"></span><span class="legend-label">{{capitalize @chartLegend.1.label}}</span>
|
||||
</div>
|
||||
|
||||
{{#if @verticalBarChartData}}
|
||||
<div data-test-monthly-usage-legend class="legend-right">
|
||||
<span class="light-dot"></span><span class="legend-label">{{capitalize @chartLegend.0.label}}</span>
|
||||
<span class="dark-dot"></span><span class="legend-label">{{capitalize @chartLegend.1.label}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
|
@ -7,7 +7,7 @@
|
|||
The total client count number is an important consideration for Vault billing.
|
||||
</p>
|
||||
</div>
|
||||
<div class="single-month-stats">
|
||||
<div class="single-month-stats" data-test-new>
|
||||
<div class="single-month-section-title">
|
||||
<StatText
|
||||
@label="New clients"
|
||||
|
@ -23,7 +23,7 @@
|
|||
<StatText @label="Non-entity clients" @value={{this.showSingleMonth.new.nonEntityClients}} @size="m" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="single-month-stats">
|
||||
<div class="single-month-stats" data-test-total>
|
||||
<div class="single-month-section-title">
|
||||
<StatText
|
||||
@label="Total monthly clients"
|
||||
|
@ -51,7 +51,7 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div class="chart-container-wide">
|
||||
<div class={{concat (unless @lineChartData "chart-empty-state ") "chart-container-wide"}}>
|
||||
<Clients::LineChart @dataset={{@lineChartData}} @upgradeData={{@upgradeData}} />
|
||||
</div>
|
||||
|
||||
|
@ -109,17 +109,19 @@
|
|||
</p>
|
||||
</div>
|
||||
|
||||
<div class="timestamp">
|
||||
<div class="timestamp" data-test-running-total-timestamp>
|
||||
{{#if @timestamp}}
|
||||
Updated
|
||||
{{date-format @timestamp "MMM d yyyy, h:mm:ss aaa"}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="legend-right">
|
||||
<span class="light-dot"></span><span class="legend-label">{{capitalize @chartLegend.0.label}}</span>
|
||||
<span class="dark-dot"></span><span class="legend-label">{{capitalize @chartLegend.1.label}}</span>
|
||||
</div>
|
||||
{{#if this.hasAverageNewClients}}
|
||||
<div class="legend-right" data-test-running-total-legend>
|
||||
<span class="light-dot"></span><span class="legend-label">{{capitalize @chartLegend.0.label}}</span>
|
||||
<span class="dark-dot"></span><span class="legend-label">{{capitalize @chartLegend.1.label}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
|
@ -8,9 +8,7 @@
|
|||
>
|
||||
</svg>
|
||||
{{else}}
|
||||
<div class="chart-empty-state">
|
||||
<EmptyState @title={{@noDataTitle}} @subTitle={{or @noDataMessage "No data to display"}} @bottomBorder={{true}} />
|
||||
</div>
|
||||
<EmptyState @title={{@noDataTitle}} @subTitle={{or @noDataMessage "No data to display"}} @bottomBorder={{true}} />
|
||||
{{/if}}
|
||||
|
||||
{{! TOOLTIP }}
|
||||
|
|
|
@ -27,9 +27,11 @@ export function formatTooltipNumber(value) {
|
|||
return new Intl.NumberFormat().format(value);
|
||||
}
|
||||
|
||||
export function calculateAverageClients(dataset, objectKey) {
|
||||
// dataset is an array of objects (consumed by the chart components)
|
||||
// objectKey is the key of the integer we want to calculate, ex: 'entity_clients', 'non_entity_clients', 'clients'
|
||||
let getIntegers = dataset.map((d) => (d[objectKey] ? d[objectKey] : 0)); // if undefined no data, so return 0
|
||||
return getIntegers.length !== 0 ? Math.round(mean(getIntegers)) : null;
|
||||
export function calculateAverage(dataset, objectKey) {
|
||||
if (!Array.isArray(dataset) || dataset?.length === 0) return null;
|
||||
// if an array of objects, objectKey of the integer we want to calculate, ex: 'entity_clients'
|
||||
// if d[objectKey] is undefined there is no value, so return 0
|
||||
const getIntegers = objectKey ? dataset?.map((d) => (d[objectKey] ? d[objectKey] : 0)) : dataset;
|
||||
let checkIntegers = getIntegers.every((n) => Number.isInteger(n)); // decimals will be false
|
||||
return checkIntegers ? Math.round(mean(getIntegers)) : null;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div
|
||||
class={{concat "stat-text-container " @size (unless @subText "-no-subText")}}
|
||||
data-test-stat-text-container
|
||||
data-test-stat-text-container={{(or @label "true")}}
|
||||
...attributes
|
||||
>
|
||||
<div class="stat-label has-bottom-margin-xs">{{@label}}</div>
|
||||
|
|
|
@ -34,7 +34,7 @@ export const parseRFC3339 = (timestamp) => {
|
|||
return date ? [`${date.getFullYear()}`, date.getMonth()] : null;
|
||||
};
|
||||
|
||||
// convert MM/yy (format of dates in charts) to 'Month yyyy' (format in tooltip)
|
||||
// convert M/yy (format of dates in charts) to 'Month yyyy' (format in tooltip)
|
||||
export function formatChartDate(date) {
|
||||
let array = date.split('/');
|
||||
array.splice(1, 0, '01');
|
||||
|
|
|
@ -1684,6 +1684,7 @@ const MOCK_MONTHLY_DATA = [
|
|||
},
|
||||
},
|
||||
{
|
||||
timestamp: formatISO(addMonths(UPGRADE_DATE, 3)),
|
||||
counts: {
|
||||
distinct_entities: 0,
|
||||
entity_clients: 10873,
|
||||
|
@ -2236,7 +2237,7 @@ const MOCK_MONTHLY_DATA = [
|
|||
},
|
||||
},
|
||||
{
|
||||
timestamp: formatISO(addMonths(UPGRADE_DATE, 3)),
|
||||
timestamp: formatISO(addMonths(UPGRADE_DATE, 4)),
|
||||
counts: {
|
||||
distinct_entities: 0,
|
||||
entity_clients: 10342,
|
||||
|
|
|
@ -9,6 +9,8 @@ import { SELECTORS, overrideResponse } from '../helpers/clients';
|
|||
import { create } from 'ember-cli-page-object';
|
||||
import ss from 'vault/tests/pages/components/search-select';
|
||||
import { clickTrigger } from 'ember-power-select/test-support/helpers';
|
||||
import { ARRAY_OF_MONTHS } from 'core/utils/date-formatters';
|
||||
|
||||
const searchSelect = create(ss);
|
||||
|
||||
const NEW_DATE = new Date();
|
||||
|
@ -133,18 +135,47 @@ module('Acceptance | clients history tab', function (hooks) {
|
|||
});
|
||||
|
||||
test('updates correctly when querying date ranges', async function (assert) {
|
||||
assert.expect(17);
|
||||
assert.expect(26);
|
||||
// TODO CMB: wire up dynamically generated activity to mirage clients handler
|
||||
// const activity = generateActivityResponse(5, LICENSE_START, LAST_MONTH);
|
||||
await visit('/vault/clients/history');
|
||||
assert.equal(currentURL(), '/vault/clients/history');
|
||||
|
||||
// change billing start month
|
||||
// query for single, historical month with no new counts
|
||||
await click(SELECTORS.rangeDropdown);
|
||||
await click('[data-test-show-calendar]');
|
||||
if (parseInt(find('[data-test-display-year]').innerText) > LICENSE_START.getFullYear()) {
|
||||
await click('[data-test-previous-year]');
|
||||
}
|
||||
await click(find(`[data-test-calendar-month=${ARRAY_OF_MONTHS[LICENSE_START.getMonth()]}]`));
|
||||
|
||||
assert.dom('[data-test-usage-stats]').exists('total usage stats show');
|
||||
assert
|
||||
.dom(SELECTORS.runningTotalMonthStats)
|
||||
.doesNotExist('running total single month stat boxes do not show');
|
||||
assert
|
||||
.dom(SELECTORS.runningTotalMonthlyCharts)
|
||||
.doesNotExist('running total month over month charts do not show');
|
||||
assert.dom(SELECTORS.monthlyUsageBlock).doesNotExist('does not show monthly usage block');
|
||||
assert.dom(SELECTORS.attributionBlock).exists('attribution area shows');
|
||||
assert
|
||||
.dom('[data-test-chart-container="new-clients"] [data-test-component="empty-state"]')
|
||||
.exists('new client attribution has empty state');
|
||||
assert
|
||||
.dom('[data-test-empty-state-subtext]')
|
||||
.hasText('There are no new clients for this namespace during this time period. ');
|
||||
assert.dom('[data-test-chart-container="total-clients"]').exists('total client attribution chart shows');
|
||||
|
||||
// reset to billing period
|
||||
await click('[data-test-popup-menu-trigger]');
|
||||
await click('[data-test-current-billing-period]');
|
||||
|
||||
// change billing start to month/year of first upgrade
|
||||
await click('[data-test-start-date-editor] button');
|
||||
await click(SELECTORS.monthDropdown);
|
||||
await click(find('.menu-list button:not([disabled])'));
|
||||
await click(find(`[data-test-date-modal-month="${ARRAY_OF_MONTHS[UPGRADE_DATE.getMonth()]}"]`));
|
||||
await click(SELECTORS.yearDropdown);
|
||||
await click(find('.menu-list button:not([disabled])'));
|
||||
await click(find(`[data-test-date-modal-year="${UPGRADE_DATE.getFullYear()}`));
|
||||
await click('[data-test-modal-save]');
|
||||
|
||||
assert.dom(SELECTORS.attributionBlock).exists('Shows attribution area');
|
||||
|
@ -154,7 +185,7 @@ module('Acceptance | clients history tab', function (hooks) {
|
|||
.exists('Shows running totals with monthly breakdown charts');
|
||||
assert
|
||||
.dom(find('[data-test-line-chart="x-axis-labels"] g.tick text'))
|
||||
.hasText('1/22', 'x-axis labels start with updated billing start month');
|
||||
.hasText(`${format(UPGRADE_DATE, 'M/yy')}`, 'x-axis labels start with updated billing start month');
|
||||
assert.equal(
|
||||
findAll('[data-test-line-chart="plot-point"]').length,
|
||||
5,
|
||||
|
@ -164,9 +195,10 @@ module('Acceptance | clients history tab', function (hooks) {
|
|||
// query custom end month
|
||||
await click(SELECTORS.rangeDropdown);
|
||||
await click('[data-test-show-calendar]');
|
||||
let readOnlyMonths = findAll('[data-test-calendar-month].is-readOnly');
|
||||
let clickableMonths = findAll('[data-test-calendar-month]').filter((m) => !readOnlyMonths.includes(m));
|
||||
await click(clickableMonths[1]);
|
||||
if (parseInt(find('[data-test-display-year]').innerText) < NEW_DATE.getFullYear()) {
|
||||
await click('[data-test-future-year]');
|
||||
}
|
||||
await click(find(`[data-test-calendar-month=${ARRAY_OF_MONTHS[LAST_MONTH.getMonth() - 2]}]`));
|
||||
|
||||
assert.dom(SELECTORS.attributionBlock).exists('Shows attribution area');
|
||||
assert.dom(SELECTORS.monthlyUsageBlock).exists('Shows monthly usage block');
|
||||
|
@ -175,23 +207,27 @@ module('Acceptance | clients history tab', function (hooks) {
|
|||
.exists('Shows running totals with monthly breakdown charts');
|
||||
assert.equal(
|
||||
findAll('[data-test-line-chart="plot-point"]').length,
|
||||
2,
|
||||
`line chart plots 2 points to match query`
|
||||
3,
|
||||
`line chart plots 3 points to match query`
|
||||
);
|
||||
let xAxisLabels = findAll('[data-test-line-chart="x-axis-labels"] g.tick text');
|
||||
assert
|
||||
.dom(findAll('[data-test-line-chart="x-axis-labels"] g.tick text')[1])
|
||||
.hasText('2/22', 'x-axis labels start with updated billing start month');
|
||||
.dom(xAxisLabels[xAxisLabels.length - 1])
|
||||
.hasText(`${format(subMonths(LAST_MONTH, 2), 'M/yy')}`, 'x-axis labels end with queried end month');
|
||||
|
||||
// query for single, historical month
|
||||
await click(SELECTORS.rangeDropdown);
|
||||
await click('[data-test-show-calendar]');
|
||||
readOnlyMonths = findAll('[data-test-calendar-month].is-readOnly');
|
||||
clickableMonths = findAll('[data-test-calendar-month]').filter((m) => !readOnlyMonths.includes(m));
|
||||
await click(clickableMonths[0]);
|
||||
if (parseInt(find('[data-test-display-year]').innerText) < NEW_DATE.getFullYear()) {
|
||||
await click('[data-test-future-year]');
|
||||
}
|
||||
await click(find(`[data-test-calendar-month=${ARRAY_OF_MONTHS[UPGRADE_DATE.getMonth()]}]`));
|
||||
|
||||
assert.dom(SELECTORS.runningTotalMonthStats).exists('running total single month stat boxes show');
|
||||
assert
|
||||
.dom(SELECTORS.runningTotalMonthlyCharts)
|
||||
.doesNotExist('running total month over month charts do not show');
|
||||
assert.dom(SELECTORS.monthlyUsageBlock).doesNotExist('Does not show monthly usage block');
|
||||
assert.dom(SELECTORS.attributionBlock).exists('attribution area shows');
|
||||
assert.dom('[data-test-chart-container="new-clients"]').exists('new client attribution chart shows');
|
||||
assert.dom('[data-test-chart-container="total-clients"]').exists('total client attribution chart shows');
|
||||
|
@ -203,8 +239,7 @@ module('Acceptance | clients history tab', function (hooks) {
|
|||
// query month older than count start date
|
||||
await click('[data-test-start-date-editor] button');
|
||||
await click(SELECTORS.yearDropdown);
|
||||
let years = findAll('.menu-list button:not([disabled])');
|
||||
await click(years[years.length - 1]);
|
||||
await click(find(`[data-test-date-modal-year="${LICENSE_START.getFullYear() - 3}`));
|
||||
await click('[data-test-modal-save]');
|
||||
|
||||
assert
|
||||
|
|
|
@ -8,20 +8,14 @@ import { Response } from 'miragejs';
|
|||
Filtering (data with mounts)
|
||||
Filtering (data without mounts)
|
||||
Filtering (data without mounts)
|
||||
|
||||
* -- HISTORY ONLY --
|
||||
* -- HISTORY ONLY --
|
||||
Filtering different date ranges (hist only)
|
||||
Upgrade warning
|
||||
No permissions for license
|
||||
Version
|
||||
queries available
|
||||
queries unavailable
|
||||
License start date this month
|
||||
*/
|
||||
|
||||
// TODO
|
||||
/*
|
||||
Filtering different date ranges (hist only)
|
||||
Upgrade warning
|
||||
|
||||
*/
|
||||
export const SELECTORS = {
|
||||
currentMonthActiveTab: '.active[data-test-current-month]',
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { find, render, findAll, triggerEvent } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
import { format, formatRFC3339, subMonths } from 'date-fns';
|
||||
import { formatChartDate } from 'core/utils/date-formatters';
|
||||
module('Integration | Component | clients/line-chart', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
const CURRENT_DATE = new Date();
|
||||
hooks.beforeEach(function () {
|
||||
this.set('xKey', 'foo');
|
||||
this.set('yKey', 'bar');
|
||||
this.set('dataset', [
|
||||
{
|
||||
foo: 1,
|
||||
|
@ -30,11 +33,179 @@ module('Integration | Component | clients/line-chart', function (hooks) {
|
|||
test('it renders', async function (assert) {
|
||||
await render(hbs`
|
||||
<div class="chart-container-wide">
|
||||
<Clients::LineChart @dataset={{dataset}} @xKey="foo" @yKey="bar" />
|
||||
<Clients::LineChart @dataset={{dataset}} @xKey={{xKey}} @yKey={{yKey}} />
|
||||
</div>
|
||||
`);
|
||||
|
||||
assert.dom('[data-test-line-chart]').exists('Chart is rendered');
|
||||
assert.dom('.hover-circle').exists({ count: 4 }, 'Renders dot for each data point');
|
||||
assert
|
||||
.dom('[data-test-line-chart="plot-point"]')
|
||||
.exists({ count: this.dataset.length }, `renders ${this.dataset.length} plot points`);
|
||||
|
||||
findAll('[data-test-line-chart="x-axis-labels"] text').forEach((e, i) => {
|
||||
assert
|
||||
.dom(e)
|
||||
.hasText(`${this.dataset[i][this.xKey]}`, `renders x-axis label: ${this.dataset[i][this.xKey]}`);
|
||||
});
|
||||
assert.dom(find('[data-test-line-chart="y-axis-labels"] text')).hasText('0', `y-axis starts at 0`);
|
||||
});
|
||||
|
||||
test('it renders upgrade data', async function (assert) {
|
||||
this.set('dataset', [
|
||||
{
|
||||
foo: format(subMonths(CURRENT_DATE, 4), 'M/yy'),
|
||||
bar: 4,
|
||||
},
|
||||
{
|
||||
foo: format(subMonths(CURRENT_DATE, 3), 'M/yy'),
|
||||
bar: 8,
|
||||
},
|
||||
{
|
||||
foo: format(subMonths(CURRENT_DATE, 2), 'M/yy'),
|
||||
bar: 14,
|
||||
},
|
||||
{
|
||||
foo: format(subMonths(CURRENT_DATE, 1), 'M/yy'),
|
||||
bar: 10,
|
||||
},
|
||||
]);
|
||||
this.set('upgradeData', [
|
||||
{
|
||||
id: '1.10.1',
|
||||
previousVersion: '1.9.2',
|
||||
timestampInstalled: formatRFC3339(subMonths(CURRENT_DATE, 2)),
|
||||
},
|
||||
]);
|
||||
await render(hbs`
|
||||
<div class="chart-container-wide">
|
||||
<Clients::LineChart
|
||||
@dataset={{dataset}}
|
||||
@upgradeData={{upgradeData}}
|
||||
@xKey={{xKey}}
|
||||
@yKey={{yKey}}
|
||||
/>
|
||||
</div>
|
||||
`);
|
||||
assert.dom('[data-test-line-chart]').exists('Chart is rendered');
|
||||
assert
|
||||
.dom('[data-test-line-chart="plot-point"]')
|
||||
.exists({ count: this.dataset.length }, `renders ${this.dataset.length} plot points`);
|
||||
assert
|
||||
.dom(find(`[data-test-line-chart="upgrade-${this.dataset[2][this.xKey]}"]`))
|
||||
.hasStyle({ opacity: '1' }, `upgrade data point ${this.dataset[2][this.xKey]} has yellow highlight`);
|
||||
});
|
||||
|
||||
test('it renders tooltip', async function (assert) {
|
||||
const tooltipData = [
|
||||
{
|
||||
month: format(subMonths(CURRENT_DATE, 4), 'M/yy'),
|
||||
clients: 4,
|
||||
new_clients: {
|
||||
clients: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
month: format(subMonths(CURRENT_DATE, 3), 'M/yy'),
|
||||
clients: 8,
|
||||
new_clients: {
|
||||
clients: 4,
|
||||
},
|
||||
},
|
||||
{
|
||||
month: format(subMonths(CURRENT_DATE, 2), 'M/yy'),
|
||||
clients: 14,
|
||||
new_clients: {
|
||||
clients: 6,
|
||||
},
|
||||
},
|
||||
{
|
||||
month: format(subMonths(CURRENT_DATE, 1), 'M/yy'),
|
||||
clients: 20,
|
||||
new_clients: {
|
||||
clients: 4,
|
||||
},
|
||||
},
|
||||
];
|
||||
this.set('dataset', tooltipData);
|
||||
this.set('upgradeData', [
|
||||
{
|
||||
id: '1.10.1',
|
||||
previousVersion: '1.9.2',
|
||||
timestampInstalled: formatRFC3339(subMonths(CURRENT_DATE, 2)),
|
||||
},
|
||||
]);
|
||||
await render(hbs`
|
||||
<div class="chart-container-wide">
|
||||
<Clients::LineChart
|
||||
@dataset={{dataset}}
|
||||
@upgradeData={{upgradeData}}
|
||||
/>
|
||||
</div>
|
||||
`);
|
||||
|
||||
const tooltipHoverCircles = findAll('[data-test-line-chart] circle.hover-circle');
|
||||
for (let [i, bar] of tooltipHoverCircles.entries()) {
|
||||
await triggerEvent(bar, 'mouseover');
|
||||
let tooltip = document.querySelector('.ember-modal-dialog');
|
||||
let { month, clients, new_clients } = tooltipData[i];
|
||||
assert
|
||||
.dom(tooltip)
|
||||
.includesText(
|
||||
`${formatChartDate(month)} ${clients} total clients ${new_clients.clients} new clients`,
|
||||
`tooltip text is correct for ${month}`
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('it fails gracefully when upgradeData is an object', async function (assert) {
|
||||
this.set('upgradeData', { some: 'object' });
|
||||
await render(hbs`
|
||||
<div class="chart-container-wide">
|
||||
<Clients::LineChart
|
||||
@dataset={{dataset}}
|
||||
@upgradeData={{upgradeData}}
|
||||
@xKey={{xKey}}
|
||||
@yKey={{yKey}}
|
||||
/>
|
||||
</div>
|
||||
`);
|
||||
|
||||
assert
|
||||
.dom('[data-test-line-chart="plot-point"]')
|
||||
.exists({ count: this.dataset.length }, 'chart still renders when upgradeData is not an array');
|
||||
});
|
||||
|
||||
test('it fails gracefully when upgradeData has incorrect key names', async function (assert) {
|
||||
this.set('upgradeData', [{ incorrect: 'key names' }]);
|
||||
await render(hbs`
|
||||
<div class="chart-container-wide">
|
||||
<Clients::LineChart
|
||||
@dataset={{dataset}}
|
||||
@upgradeData={{upgradeData}}
|
||||
@xKey={{xKey}}
|
||||
@yKey={{yKey}}
|
||||
/>
|
||||
</div>
|
||||
`);
|
||||
|
||||
assert
|
||||
.dom('[data-test-line-chart="plot-point"]')
|
||||
.exists({ count: this.dataset.length }, 'chart still renders when upgradeData has incorrect keys');
|
||||
});
|
||||
|
||||
test('it renders empty state when no dataset', async function (assert) {
|
||||
await render(hbs`
|
||||
<div class="chart-container-wide">
|
||||
<Clients::LineChart @noDataMessage="this is a custom message to explain why you're not seeing a line chart"/>
|
||||
</div>
|
||||
`);
|
||||
|
||||
assert.dom('[data-test-component="empty-state"]').exists('renders empty state when no data');
|
||||
assert
|
||||
.dom('[data-test-empty-state-subtext]')
|
||||
.hasText(
|
||||
`this is a custom message to explain why you're not seeing a line chart`,
|
||||
'custom message renders'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { render, findAll, find, triggerEvent } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | clients/vertical-bar-chart', function (hooks) {
|
||||
|
@ -12,19 +12,96 @@ module('Integration | Component | clients/vertical-bar-chart', function (hooks)
|
|||
]);
|
||||
});
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
test('it renders chart and tooltip for total clients', async function (assert) {
|
||||
const barChartData = [
|
||||
{ month: 'january', clients: 200, entity_clients: 91, non_entity_clients: 50, new_clients: 5 },
|
||||
{ month: 'february', clients: 300, entity_clients: 101, non_entity_clients: 150, new_clients: 5 },
|
||||
{ month: 'january', clients: 141, entity_clients: 91, non_entity_clients: 50, new_clients: 5 },
|
||||
{ month: 'february', clients: 251, entity_clients: 101, non_entity_clients: 150, new_clients: 5 },
|
||||
];
|
||||
this.set('barChartData', barChartData);
|
||||
|
||||
await render(hbs`
|
||||
<div class="chart-container-wide">
|
||||
<Clients::VerticalBarChart
|
||||
@dataset={{barChartData}}
|
||||
@chartLegend={{chartLegend}}
|
||||
/>
|
||||
</div>
|
||||
`);
|
||||
assert.dom('[data-test-vertical-bar-chart]').exists();
|
||||
const tooltipHoverBars = findAll('[data-test-vertical-bar-chart] rect.tooltip-rect');
|
||||
assert.dom('[data-test-vertical-bar-chart]').exists('renders chart');
|
||||
assert
|
||||
.dom('[data-test-vertical-chart="data-bar"]')
|
||||
.exists({ count: barChartData.length * 2 }, 'renders correct number of bars'); // multiply length by 2 because bars are stacked
|
||||
|
||||
assert.dom(find('[data-test-vertical-chart="y-axis-labels"] text')).hasText('0', `y-axis starts at 0`);
|
||||
findAll('[data-test-vertical-chart="x-axis-labels"] text').forEach((e, i) => {
|
||||
assert.dom(e).hasText(`${barChartData[i].month}`, `renders x-axis label: ${barChartData[i].month}`);
|
||||
});
|
||||
|
||||
for (let [i, bar] of tooltipHoverBars.entries()) {
|
||||
await triggerEvent(bar, 'mouseover');
|
||||
let tooltip = document.querySelector('.ember-modal-dialog');
|
||||
assert
|
||||
.dom(tooltip)
|
||||
.includesText(
|
||||
`${barChartData[i].clients} total clients ${barChartData[i].entity_clients} entity clients ${barChartData[i].non_entity_clients} non-entity clients`,
|
||||
'tooltip text is correct'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('it renders chart and tooltip for new clients', async function (assert) {
|
||||
const barChartData = [
|
||||
{ month: 'january', entity_clients: 91, non_entity_clients: 50, clients: 0 },
|
||||
{ month: 'february', entity_clients: 101, non_entity_clients: 150, clients: 110 },
|
||||
];
|
||||
this.set('barChartData', barChartData);
|
||||
|
||||
await render(hbs`
|
||||
<div class="chart-container-wide">
|
||||
<Clients::VerticalBarChart
|
||||
@dataset={{barChartData}}
|
||||
@chartLegend={{chartLegend}}
|
||||
/>
|
||||
</div>
|
||||
`);
|
||||
|
||||
const tooltipHoverBars = findAll('[data-test-vertical-bar-chart] rect.tooltip-rect');
|
||||
assert.dom('[data-test-vertical-bar-chart]').exists('renders chart');
|
||||
assert
|
||||
.dom('[data-test-vertical-chart="data-bar"]')
|
||||
.exists({ count: barChartData.length * 2 }, 'renders correct number of bars'); // multiply length by 2 because bars are stacked
|
||||
|
||||
assert.dom(find('[data-test-vertical-chart="y-axis-labels"] text')).hasText('0', `y-axis starts at 0`);
|
||||
findAll('[data-test-vertical-chart="x-axis-labels"] text').forEach((e, i) => {
|
||||
assert.dom(e).hasText(`${barChartData[i].month}`, `renders x-axis label: ${barChartData[i].month}`);
|
||||
});
|
||||
|
||||
for (let [i, bar] of tooltipHoverBars.entries()) {
|
||||
await triggerEvent(bar, 'mouseover');
|
||||
let tooltip = document.querySelector('.ember-modal-dialog');
|
||||
assert
|
||||
.dom(tooltip)
|
||||
.includesText(
|
||||
`${barChartData[i].clients} new clients ${barChartData[i].entity_clients} entity clients ${barChartData[i].non_entity_clients} non-entity clients`,
|
||||
'tooltip text is correct'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('it renders empty state when no dataset', async function (assert) {
|
||||
await render(hbs`
|
||||
<div class="chart-container-wide">
|
||||
<Clients::VerticalBarChart @noDataMessage="this is a custom message to explain why you're not seeing a vertical bar chart"/>
|
||||
</div>
|
||||
`);
|
||||
|
||||
assert.dom('[data-test-component="empty-state"]').exists('renders empty state when no data');
|
||||
assert
|
||||
.dom('[data-test-empty-state-subtext]')
|
||||
.hasText(
|
||||
`this is a custom message to explain why you're not seeing a vertical bar chart`,
|
||||
'custom message renders'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { formatNumbers, formatTooltipNumber } from 'vault/utils/chart-helpers';
|
||||
import { formatNumbers, formatTooltipNumber, calculateAverage } from 'vault/utils/chart-helpers';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
const SMALL_NUMBERS = [0, 7, 27, 103, 999];
|
||||
|
@ -28,4 +28,35 @@ module('Unit | Utility | chart-helpers', function () {
|
|||
const formatted = formatTooltipNumber(120300200100);
|
||||
assert.equal(formatted.length, 15, 'adds punctuation at proper place for large numbers');
|
||||
});
|
||||
|
||||
test('calculateAverage is accurate', function (assert) {
|
||||
const testArray1 = [
|
||||
{ label: 'foo', value: 10 },
|
||||
{ label: 'bar', value: 22 },
|
||||
];
|
||||
const testArray2 = [
|
||||
{ label: 'foo', value: undefined },
|
||||
{ label: 'bar', value: 22 },
|
||||
];
|
||||
const getAverage = (array) => array.reduce((a, b) => a + b, 0) / array.length;
|
||||
assert.equal(calculateAverage(null), null, 'returns null if dataset it null');
|
||||
assert.equal(calculateAverage([]), null, 'returns null if dataset it empty array');
|
||||
assert.equal(calculateAverage([0]), getAverage([0]), `returns ${getAverage([0])} if array is just 0 0`);
|
||||
assert.equal(calculateAverage([1]), getAverage([1]), `returns ${getAverage([1])} if array is just 1`);
|
||||
assert.equal(
|
||||
calculateAverage([5, 1, 41, 5]),
|
||||
getAverage([5, 1, 41, 5]),
|
||||
`returns correct average for array of integers`
|
||||
);
|
||||
assert.equal(
|
||||
calculateAverage(testArray1, 'value'),
|
||||
getAverage([10, 22]),
|
||||
`returns correct average for array of objects`
|
||||
);
|
||||
assert.equal(
|
||||
calculateAverage(testArray2, 'value'),
|
||||
getAverage([0, 22]),
|
||||
`returns correct average for array of objects containing undefined values`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue