mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-22 07:01:09 +02:00
* Client Count Routing Updates (#24733) * updates client count routing for sync and future additions * adds copyright header to clients sync template * adds missing copyright headers * UI: Adds secret_syncs to mirage /activity endpoint (#24846) * add secret_syncs to mirage endpoint * import clients handler * UI: Set up client charts for incoming sync data (#24852) * sum stacked bar values for tooltip total * make tooltip dynamic based on chartLegend * remove redundant helper * add secret_syncs to client count utils * move sum function to helper * update horizontal bar chart to include sync_clients * calculate sum of bars in tooltip * rename color palette const, define chart legends in each parent component instead of token.js * update tooltips * update mirage handler to add sys/ namespace * update mirage handler to add sys/ namespace * use pushObject * update test * UI: Secret sync bar chart (#24926) * install lineal * add ember-style-modifier dep * Add client count types for serialized data * Add sync bar chart component with tests * Chart is responsive * address comments * Clients Counts Parent Route (#24899) * adds interfaces for clients models * moves date formatting logic from clients activity adapter to utils file * adds clients counts route * updates links to clients route to point to top level and updates redirect to counts overview route * removes clients base route and moves overview and sync routes under counts * adds clients counts page component * converts clients route to ts * adds billing start timestamp to clients config mirage response and updates counts route to always attempt to fetch activity * fixes issue with updating namespace and auth mount query params always triggering client counts route model hook * adds tests for clients counts page component * adds missing copyright header to client-counts type file * Update ui/app/components/clients/page/counts.hbs Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> * fixes bad import in sync-bar-chart * updates clients counts route to bypass query if there is not start_time * pins d3-shape to 1.3.7 for now -- makes lineal play nice with old charts * fixes sync bar chart tooltip assertion --------- Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> * UI: convert line-chart to lineal (#24961) * lineal chart alongside svg * Add version-history to sync handler for testing * line chart is TS, test updated * remove d3-shape resolution * fix clients/token-test * use chartHeight in running-total template * use M/yy key instead of timestamp, chart is responsive * Add test for swapping datasets * add more edge case tests * more test * remove untrue assertion * fix weird decimal when between 1.1k and 2k * address feedback * Update line-chart to use timestamp instead of month key * Add timestamp to all places where month is on the clients activity response * Client Counts Overview (#24969) * adds counts base component for use in client counts child routes * adds clients counts overview page component * splits out monthly new chart from clients running total component * adds missing copyright headers * moves running total related assertions from token to overview acceptance test * removes new client assertions from running-total test and adds tests for monthly-new component * updates copy in running-total component * fixes clients overview tests * fixes timestamp stub not being restored in monthly-new test * fixes mfa-login test * renames counts component to activity * removes unused selectedAuthMethod arg from running-total component * adds timestamp back to running-total component * Secrets sync UI: add sync page component (#24982) * adds counts base component for use in client counts child routes * adds clients counts overview page component * splits out monthly new chart from clients running total component * adds missing copyright headers * move sync-bar-chart to charts/ folder * update types and rename chart * rename template file * moves running total related assertions from token to overview acceptance test * removes new client assertions from running-total test and adds tests for monthly-new component * updates copy in running-total component * fixes clients overview tests * fixes timestamp stub not being restored in monthly-new test * fixes mfa-login test * fix 0 values erroring charts * separate timestamp again * address merge conflicts * finish building sync chart component WIP css * renames counts component to activity * update import * revert name to dataKey * update styling for charts without legends * use monthly stat chart component for layout * use monthly chart stats in monthly new * implement stat wrapper; * remove extra grid div * rename component * fix legend css; * update test[ * remove arbitrarily setting max * add single month view * use stat text * update line chart tests * rename line chart * update tests --------- Co-authored-by: Jordan Reimer <zofskeez@gmail.com> * update selectors * add sync page tests * Secrets Sync UI: Add secrets syncs to csv export (#25056) * update mirage and add sync clients to export csv * fix sync legend label * remove word * update copy in modal * update mirage * fix attribution tooltip text * Clients Counts Token Route (#25019) * renames token route and page component back to dashboard * adds client counts token route and page component * updates charts in token page to use ChartContainer component * adds tests for clients token page component * restore clients dashboard test * use var for chart title sync page * updates clients token page to show usage stats when querying single month * updates token page clients averages to only include entity and non-entity clients in calculation * fixes monthly total counts lower than new clients in mirage handler * fixes token test --------- Co-authored-by: clairebontempo@gmail.com <clairebontempo@gmail.com> * Clients Usage Stats/Running Total Updates (#25094) * updates clients usage counts and running totals * updates usage stats total copy * fixes client counts overview tests * Secrets sync UI: cleanup and consolidation of components (#25090) * rename authMethod to mountPath * generalize count template copy * add todo to delete monthly new component * rename to tokenTab * wrap filters in conditional checking for start timestamp * some users may not have access to /config endpoint * fix querying when user has no billing date permissions and clicks current billing period * extend activity component from counts page * Revert "extend activity component from counts page" This reverts commit 1d0e85c82faf88c4385a04b1a5841cdde7fd00e0. * rename to startTimestampISO * remove timestamp from route and just use activity model responseTimestamp * fix chart y domain max * fix typos in usage stat and running totals component * delete backing class for display only template; * updates tests * adds comment for fetching license to get start date for billing * cleans up unused client counts files (#25157) * adds changelog * fix assertion copy * adds changelog description * updates enterprise sidebar nav test --------- Co-authored-by: clairebontempo@gmail.com <clairebontempo@gmail.com> Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com> Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com>
400 lines
11 KiB
JavaScript
400 lines
11 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
import { module, test } from 'qunit';
|
|
import sinon from 'sinon';
|
|
import { setupRenderingTest } from 'ember-qunit';
|
|
import { find, render, findAll } from '@ember/test-helpers';
|
|
import { hbs } from 'ember-cli-htmlbars';
|
|
import { format, formatRFC3339, subMonths } from 'date-fns';
|
|
import timestamp from 'core/utils/timestamp';
|
|
|
|
module('Integration | Component | clients/line-chart', function (hooks) {
|
|
setupRenderingTest(hooks);
|
|
hooks.before(function () {
|
|
sinon.stub(timestamp, 'now').callsFake(() => new Date('2018-04-03T14:15:30'));
|
|
});
|
|
hooks.beforeEach(function () {
|
|
this.set('xKey', 'foo');
|
|
this.set('yKey', 'bar');
|
|
this.set('dataset', [
|
|
{
|
|
foo: '2018-04-03T14:15:30',
|
|
bar: 4,
|
|
expectedLabel: '4/18',
|
|
},
|
|
{
|
|
foo: '2018-05-03T14:15:30',
|
|
bar: 8,
|
|
expectedLabel: '5/18',
|
|
},
|
|
{
|
|
foo: '2018-06-03T14:15:30',
|
|
bar: 14,
|
|
expectedLabel: '6/18',
|
|
},
|
|
{
|
|
foo: '2018-07-03T14:15:30',
|
|
bar: 10,
|
|
expectedLabel: '7/18',
|
|
},
|
|
]);
|
|
});
|
|
hooks.after(function () {
|
|
timestamp.now.restore();
|
|
});
|
|
|
|
test('it renders', async function (assert) {
|
|
await render(hbs`
|
|
<div class="chart-container-wide">
|
|
<Clients::Charts::Line @dataset={{this.dataset}} @xKey={{this.xKey}} @yKey={{this.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`);
|
|
findAll('[data-test-x-axis] text').forEach((e, i) => {
|
|
// For some reason the first axis label is not rendered
|
|
assert
|
|
.dom(e)
|
|
.hasText(
|
|
`${this.dataset[i].expectedLabel}`,
|
|
`renders x-axis label: ${this.dataset[i].expectedLabel}`
|
|
);
|
|
});
|
|
assert.dom('[data-test-y-axis] text').hasText('0', `y-axis starts at 0`);
|
|
});
|
|
|
|
test('it renders upgrade data', async function (assert) {
|
|
const now = timestamp.now();
|
|
this.set('dataset', [
|
|
{
|
|
foo: formatRFC3339(subMonths(now, 4)),
|
|
bar: 4,
|
|
month: format(subMonths(now, 4), 'M/yy'),
|
|
},
|
|
{
|
|
foo: formatRFC3339(subMonths(now, 3)),
|
|
bar: 8,
|
|
month: format(subMonths(now, 3), 'M/yy'),
|
|
},
|
|
{
|
|
foo: formatRFC3339(subMonths(now, 2)),
|
|
bar: 14,
|
|
month: format(subMonths(now, 2), 'M/yy'),
|
|
},
|
|
{
|
|
foo: formatRFC3339(subMonths(now, 1)),
|
|
bar: 10,
|
|
month: format(subMonths(now, 1), 'M/yy'),
|
|
},
|
|
]);
|
|
this.set('upgradeData', [
|
|
{
|
|
id: '1.10.1',
|
|
previousVersion: '1.9.2',
|
|
timestampInstalled: formatRFC3339(subMonths(now, 2)),
|
|
},
|
|
]);
|
|
await render(hbs`
|
|
<div class="chart-container-wide">
|
|
<Clients::Charts::Line
|
|
@dataset={{this.dataset}}
|
|
@upgradeData={{this.upgradeData}}
|
|
@xKey={{this.xKey}}
|
|
@yKey={{this.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].month}"]`))
|
|
.hasStyle(
|
|
{ fill: 'rgb(253, 238, 186)' },
|
|
`upgrade data point ${this.dataset[2].month} has yellow highlight`
|
|
);
|
|
});
|
|
|
|
test('it renders tooltip', async function (assert) {
|
|
assert.expect(1);
|
|
const now = timestamp.now();
|
|
const tooltipData = [
|
|
{
|
|
month: format(subMonths(now, 4), 'M/yy'),
|
|
timestamp: formatRFC3339(subMonths(now, 4)),
|
|
clients: 4,
|
|
new_clients: {
|
|
clients: 0,
|
|
},
|
|
},
|
|
{
|
|
month: format(subMonths(now, 3), 'M/yy'),
|
|
timestamp: formatRFC3339(subMonths(now, 3)),
|
|
clients: 8,
|
|
new_clients: {
|
|
clients: 4,
|
|
},
|
|
},
|
|
{
|
|
month: format(subMonths(now, 2), 'M/yy'),
|
|
timestamp: formatRFC3339(subMonths(now, 2)),
|
|
clients: 14,
|
|
new_clients: {
|
|
clients: 6,
|
|
},
|
|
},
|
|
{
|
|
month: format(subMonths(now, 1), 'M/yy'),
|
|
timestamp: formatRFC3339(subMonths(now, 1)),
|
|
clients: 20,
|
|
new_clients: {
|
|
clients: 4,
|
|
},
|
|
},
|
|
];
|
|
this.set('dataset', tooltipData);
|
|
this.set('upgradeData', [
|
|
{
|
|
id: '1.10.1',
|
|
previousVersion: '1.9.2',
|
|
timestampInstalled: formatRFC3339(subMonths(now, 2)),
|
|
},
|
|
]);
|
|
await render(hbs`
|
|
<div class="chart-container-wide">
|
|
<Clients::Charts::Line
|
|
@dataset={{this.dataset}}
|
|
@upgradeData={{this.upgradeData}}
|
|
/>
|
|
</div>
|
|
`);
|
|
|
|
const tooltipHoverCircles = findAll('[data-test-hover-circle]');
|
|
assert.strictEqual(tooltipHoverCircles.length, tooltipData.length, 'all data circles are rendered');
|
|
|
|
// FLAKY after adding a11y testing, skip for now
|
|
// for (const [i, bar] of tooltipHoverCircles.entries()) {
|
|
// await triggerEvent(bar, 'mouseover');
|
|
// const tooltip = document.querySelector('.ember-modal-dialog');
|
|
// const { 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 data is not formatted correctly', async function (assert) {
|
|
this.set('dataset', [
|
|
{
|
|
foo: 1,
|
|
bar: 4,
|
|
},
|
|
{
|
|
foo: 2,
|
|
bar: 8,
|
|
},
|
|
{
|
|
foo: 3,
|
|
bar: 14,
|
|
},
|
|
{
|
|
foo: 4,
|
|
bar: 10,
|
|
},
|
|
]);
|
|
await render(hbs`
|
|
<div class="chart-container-wide">
|
|
<Clients::Charts::Line
|
|
@dataset={{this.dataset}}
|
|
@xKey={{this.xKey}}
|
|
@yKey={{this.yKey}}
|
|
/>
|
|
</div>
|
|
`);
|
|
|
|
assert.dom('[data-test-line-chart]').doesNotExist('Chart is not rendered');
|
|
assert
|
|
.dom('[data-test-component="empty-state"]')
|
|
.hasText('No data to display', 'Shows empty state when time date is not formatted correctly');
|
|
});
|
|
|
|
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::Charts::Line
|
|
@dataset={{this.dataset}}
|
|
@upgradeData={{this.upgradeData}}
|
|
@xKey={{this.xKey}}
|
|
@yKey={{this.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::Charts::Line
|
|
@dataset={{this.dataset}}
|
|
@upgradeData={{this.upgradeData}}
|
|
@xKey={{this.xKey}}
|
|
@yKey={{this.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::Charts::Line @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'
|
|
);
|
|
});
|
|
|
|
test('it updates axis when dataset updates', async function (assert) {
|
|
const datasets = {
|
|
small: [
|
|
{
|
|
foo: '2020-04-01',
|
|
bar: 4,
|
|
month: '4/20',
|
|
},
|
|
{
|
|
foo: '2020-05-01',
|
|
bar: 8,
|
|
month: '5/20',
|
|
},
|
|
{
|
|
foo: '2020-06-01',
|
|
bar: 1,
|
|
},
|
|
{
|
|
foo: '2020-07-01',
|
|
bar: 10,
|
|
},
|
|
],
|
|
large: [
|
|
{
|
|
foo: '2020-08-01',
|
|
bar: 4586,
|
|
month: '8/20',
|
|
},
|
|
{
|
|
foo: '2020-09-01',
|
|
bar: 8928,
|
|
month: '9/20',
|
|
},
|
|
{
|
|
foo: '2020-10-01',
|
|
bar: 11948,
|
|
month: '10/20',
|
|
},
|
|
{
|
|
foo: '2020-11-01',
|
|
bar: 16943,
|
|
month: '11/20',
|
|
},
|
|
],
|
|
broken: [
|
|
{
|
|
foo: '2020-01-01',
|
|
bar: null,
|
|
month: '1/20',
|
|
},
|
|
{
|
|
foo: '2020-02-01',
|
|
bar: 0,
|
|
month: '2/20',
|
|
},
|
|
{
|
|
foo: '2020-03-01',
|
|
bar: 22,
|
|
month: '3/20',
|
|
},
|
|
{
|
|
foo: '2020-04-01',
|
|
bar: null,
|
|
month: '4/20',
|
|
},
|
|
{
|
|
foo: '2020-05-01',
|
|
bar: 70,
|
|
month: '5/20',
|
|
},
|
|
{
|
|
foo: '2020-06-01',
|
|
bar: 50,
|
|
month: '6/20',
|
|
},
|
|
],
|
|
};
|
|
this.set('dataset', datasets.small);
|
|
await render(hbs`
|
|
<div class="chart-container-wide">
|
|
<Clients::Charts::Line
|
|
@dataset={{this.dataset}}
|
|
@upgradeData={{this.upgradeData}}
|
|
@xKey={{this.xKey}}
|
|
@yKey={{this.yKey}}
|
|
/>
|
|
</div>
|
|
`);
|
|
assert.dom('[data-test-y-axis]').hasText('0 2 4 6 8 10', 'y-axis renders correctly for small values');
|
|
assert
|
|
.dom('[data-test-x-axis]')
|
|
.hasText('4/20 5/20 6/20 7/20', 'x-axis renders correctly for small values');
|
|
|
|
// Update to large dataset
|
|
this.set('dataset', datasets.large);
|
|
assert.dom('[data-test-y-axis]').hasText('0 5k 10k 15k', 'y-axis renders correctly for new large values');
|
|
assert
|
|
.dom('[data-test-x-axis]')
|
|
.hasText('8/20 9/20 10/20 11/20', 'x-axis renders correctly for small values');
|
|
|
|
// Update to broken dataset
|
|
this.set('dataset', datasets.broken);
|
|
assert.dom('[data-test-y-axis]').hasText('0 20 40 60', 'y-axis renders correctly for new broken values');
|
|
assert
|
|
.dom('[data-test-x-axis]')
|
|
.hasText('1/20 2/20 3/20 4/20 5/20 6/20', 'x-axis renders correctly for small values');
|
|
assert.dom('[data-test-hover-circle]').exists({ count: 4 }, 'only render circles for non-null values');
|
|
|
|
assert
|
|
.dom('[data-test-hover-circle="1/20"]')
|
|
.doesNotExist('first month dot does not exist because value is null');
|
|
assert
|
|
.dom('[data-test-hover-circle="4/20"]')
|
|
.doesNotExist('other null count month dot also does not render');
|
|
// Note: the line should also show a gap, but this is difficult to test for
|
|
});
|
|
});
|