open-vault/ui/tests/acceptance/client-dashboard-test.js
Jordan Reimer c84d267c61
Sidebar Navigation (#19296)
* Add Helios Design System Components (#19278)

* adds hds dependency

* updates reset import path

* sets minifyCSS advanced option to false

* Remove node-sass (#19376)

* removes node-sass and fixes sass compilation

* fixes active tab li class

* Sidebar Navigation Components (#19446)

* links ember-shared-components addon and imports styles

* adds sidebar frame and nav components

* updates HcNav component name to HcAppFrame and adds sidebar UserMenu component

* adds tests for sidebar components

* fixes tests

* updates user menu styling

* fixes typos in nav cluster component

* changes padding value in sidebar stylesheet to use variable

* Replace and remove old nav components with new ones (#19447)

* links ember-shared-components addon and imports styles

* adds sidebar frame and nav components

* updates activeCluster on auth service and adds activeSession prop for sidebar visibility

* replaces old nav components with new ones in templates

* fixes sidebar visibility issue and updates user menu label class

* removes NavHeader usage

* adds clients index route to redirect to dashboard

* removes unused HcAppFrame footer block and reduces page header top margin

* Nav component cleanup (#19681)

* removes nav-header components

* removes navbar styling

* removes status-menu component and styles

* removes cluster and auth info components

* removes menu-sidebar component and styling

* fixes tests

* Console Panel Updates (#19741)

* updates console panel styling

* adds test for opening and closing the console panel

* updates console panel background color to use hds token

* adds right margin to console panel input

* updates link-status banner styling

* updates hc nav components to new API

* Namespace Picker Updates (#19753)

* updates namespace-picker

* updates namespace picker menu styling

* adds bottom margin to env banner

* updates class order on namespace picker link

* restores manage namespaces refresh icon

* removes manage namespaces nav icon

* removes home link component (#20027)

* Auth and Error View Updates (#19749)

* adds vault logo to auth page

* updates top level error template

* updates loading substate handling and moves policies link from access to cluster nav (#20033)

* moves console panel to bottom of viewport (#20183)

* HDS Sidebar Nav Components (#20197)

* updates nav components to hds

* upgrades project yarn version to 3.5

* fixes issues in app frame component

* updates sidenav actions to use icon button component

* Sidebar navigation acceptance tests (#20270)

* adds sidebar navigation acceptance tests and fixes other test failures

* console panel styling tweaks

* bumps addon version

* remove and ignore yarn install-state file

* fixes auth service and console tests

* moves classes from deleted files after bulma merge

* fixes sass syntax errors blocking build

* cleans up dart sass deprecation warnings

* adds changelog entry

* hides namespace picker when sidebar nav panel is minimized

* style tweaks

* fixes sidebar nav tests

* bumps hds addon to latest version and removes style override

* updates modify-passthrough-response helper

* updates sidebar nav tests

* mfa-setup test fix attempt

* fixes cluster mfa setup test

* remove deprecated yarn ignore-optional flag from makefile

* removes another instance of yarn ignore-optional and updates ui readme

* removes unsupported yarn verbose flag from ci-helper

* hides nav headings when user does not have access to any sub links

* removes unused optional deps and moves lint-staged to dev deps

* updates has-permission helper and permissions service tests

* fixes issue with console panel not filling container width
2023-05-02 19:36:15 -06:00

394 lines
17 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/
import { module, test } from 'qunit';
import sinon from 'sinon';
import { visit, currentURL, click, findAll, find, settled } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import authPage from 'vault/tests/pages/auth';
import { addMonths, formatRFC3339, startOfMonth, subMonths } from 'date-fns';
import { setupMirage } from 'ember-cli-mirage/test-support';
import ENV from 'vault/config/environment';
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';
import { formatNumber } from 'core/helpers/format-number';
import timestamp from 'core/utils/timestamp';
const searchSelect = create(ss);
const STATIC_NOW = new Date('2023-01-13T14:15:00');
// for testing, we're in the middle of a license/billing period
const LICENSE_START = startOfMonth(subMonths(STATIC_NOW, 6)); // 2022-07-01
// upgrade happened 1 month after license start
const UPGRADE_DATE = addMonths(LICENSE_START, 1); // 2022-08-01
module('Acceptance | client counts dashboard tab', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
hooks.before(function () {
sinon.stub(timestamp, 'now').callsFake(() => STATIC_NOW);
ENV['ember-cli-mirage'].handler = 'clients';
});
hooks.beforeEach(function () {
this.store = this.owner.lookup('service:store');
});
hooks.after(function () {
timestamp.now.restore();
ENV['ember-cli-mirage'].handler = null;
});
hooks.beforeEach(function () {
return authPage.login();
});
test('shows warning when config off, no data', async function (assert) {
assert.expect(4);
this.server.get('sys/internal/counters/activity', () => overrideResponse(204));
this.server.get('sys/internal/counters/config', () => {
return {
request_id: 'some-config-id',
data: {
default_report_months: 12,
enabled: 'default-disable',
queries_available: false,
retention_months: 24,
},
};
});
await visit('/vault/clients/dashboard');
assert.strictEqual(currentURL(), '/vault/clients/dashboard');
assert.dom(SELECTORS.dashboardActiveTab).hasText('Dashboard', 'dashboard tab is active');
assert.dom(SELECTORS.emptyStateTitle).hasText('Data tracking is disabled');
assert.dom(SELECTORS.filterBar).doesNotExist('Filter bar is hidden when no data available');
});
test('shows empty state when config enabled and no data', async function (assert) {
assert.expect(4);
this.server.get('sys/internal/counters/activity', () => overrideResponse(204));
this.server.get('sys/internal/counters/config', () => {
return {
request_id: 'some-config-id',
data: {
default_report_months: 12,
enabled: 'default-enable',
retention_months: 24,
},
};
});
await visit('/vault/clients/dashboard');
assert.strictEqual(currentURL(), '/vault/clients/dashboard');
assert.dom(SELECTORS.dashboardActiveTab).hasText('Dashboard', 'dashboard tab is active');
assert.dom(SELECTORS.emptyStateTitle).hasTextContaining('No data received');
assert.dom(SELECTORS.filterBar).doesNotExist('Does not show filter bar');
});
test('visiting dashboard tab config on and data with mounts', async function (assert) {
assert.expect(8);
await visit('/vault/clients/dashboard');
assert.strictEqual(currentURL(), '/vault/clients/dashboard');
assert
.dom(SELECTORS.dateDisplay)
.hasText('July 2022', 'billing start month is correctly parsed from license');
assert
.dom(SELECTORS.rangeDropdown)
.hasText(`Jul 2022 - Jan 2023`, 'Date range shows dates correctly parsed activity response');
assert.dom(SELECTORS.attributionBlock).exists('Shows attribution area');
assert.dom(SELECTORS.monthlyUsageBlock).exists('Shows monthly usage block');
assert
.dom(SELECTORS.runningTotalMonthlyCharts)
.exists('Shows running totals with monthly breakdown charts');
assert
.dom(find('[data-test-line-chart="x-axis-labels"] g.tick text'))
.hasText(`7/22`, 'x-axis labels start with billing start date');
assert.strictEqual(
findAll('[data-test-line-chart="plot-point"]').length,
6,
`line chart plots 6 points to match query`
);
});
test('updates correctly when querying date ranges', async function (assert) {
assert.expect(27);
await visit('/vault/clients/dashboard');
assert.strictEqual(currentURL(), '/vault/clients/dashboard');
// 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(SELECTORS.rangeDropdown);
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(`[data-test-dropdown-month="${ARRAY_OF_MONTHS[UPGRADE_DATE.getMonth()]}"]`);
await click(SELECTORS.yearDropdown);
await click(`[data-test-dropdown-year="${UPGRADE_DATE.getFullYear()}"]`);
await click('[data-test-date-dropdown-submit]');
assert.dom(SELECTORS.attributionBlock).exists('Shows attribution area');
assert.dom(SELECTORS.monthlyUsageBlock).exists('Shows monthly usage block');
assert
.dom(SELECTORS.runningTotalMonthlyCharts)
.exists('Shows running totals with monthly breakdown charts');
assert
.dom(find('[data-test-line-chart="x-axis-labels"] g.tick text'))
.hasText(`8/22`, 'x-axis labels start with updated billing start month');
assert.strictEqual(
findAll('[data-test-line-chart="plot-point"]').length,
6,
`line chart plots 6 points to match query`
);
// query three months ago (Oct 2022)
await click(SELECTORS.rangeDropdown);
await click('[data-test-show-calendar]');
await click('[data-test-previous-year]');
await click(find(`[data-test-calendar-month="October"]`));
assert.dom(SELECTORS.attributionBlock).exists('Shows attribution area');
assert.dom(SELECTORS.monthlyUsageBlock).exists('Shows monthly usage block');
assert
.dom(SELECTORS.runningTotalMonthlyCharts)
.exists('Shows running totals with monthly breakdown charts');
assert.strictEqual(
findAll('[data-test-line-chart="plot-point"]').length,
3,
`line chart plots 3 points to match query`
);
const xAxisLabels = findAll('[data-test-line-chart="x-axis-labels"] g.tick text');
assert
.dom(xAxisLabels[xAxisLabels.length - 1])
.hasText(`10/22`, 'x-axis labels end with queried end month');
// query for single, historical month (upgrade month)
await click(SELECTORS.rangeDropdown);
await click('[data-test-show-calendar]');
assert.dom('[data-test-display-year]').hasText('2022');
await click(find(`[data-test-calendar-month="August"]`));
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');
// reset to billing period
await click(SELECTORS.rangeDropdown);
await click('[data-test-current-billing-period]');
// query month older than count start date
await click('[data-test-start-date-editor] button');
await click(SELECTORS.monthDropdown);
await click(`[data-test-dropdown-month="${ARRAY_OF_MONTHS[LICENSE_START.getMonth()]}"]`);
await click(SELECTORS.yearDropdown);
await click(`[data-test-dropdown-year="${LICENSE_START.getFullYear() - 3}"]`);
await click('[data-test-date-dropdown-submit]');
assert
.dom('[data-test-alert-banner="alert"]')
.hasTextContaining(
`We only have data from January 2022`,
'warning banner displays that date queried was prior to count start date'
);
});
test('dashboard filters correctly with full data', async function (assert) {
assert.expect(21);
await visit('/vault/clients/dashboard');
assert.strictEqual(currentURL(), '/vault/clients/dashboard', 'clients/dashboard URL is correct');
assert.dom(SELECTORS.dashboardActiveTab).hasText('Dashboard', 'dashboard tab is active');
assert
.dom(SELECTORS.runningTotalMonthlyCharts)
.exists('Shows running totals with monthly breakdown charts');
assert.dom(SELECTORS.attributionBlock).exists('Shows attribution area');
assert.dom(SELECTORS.monthlyUsageBlock).exists('Shows monthly usage block');
const response = await this.store.peekRecord('clients/activity', 'some-activity-id');
// FILTER BY NAMESPACE
await clickTrigger();
await searchSelect.options.objectAt(0).click();
await settled();
const topNamespace = response.byNamespace[0];
const topMount = topNamespace.mounts[0];
assert.ok(true, 'Filter by first namespace');
assert.strictEqual(
find(SELECTORS.selectedNs).innerText.toLowerCase(),
topNamespace.label,
'selects top namespace'
);
assert.dom('[data-test-top-attribution]').includesText('Top auth method');
assert
.dom('[data-test-running-total-entity] p')
.includesText(`${formatNumber([topNamespace.entity_clients])}`, 'total entity clients is accurate');
assert
.dom('[data-test-running-total-nonentity] p')
.includesText(
`${formatNumber([topNamespace.non_entity_clients])}`,
'total non-entity clients is accurate'
);
assert
.dom('[data-test-attribution-clients] p')
.includesText(`${formatNumber([topMount.clients])}`, 'top attribution clients accurate');
// FILTER BY AUTH METHOD
await clickTrigger();
await searchSelect.options.objectAt(0).click();
await settled();
assert.ok(true, 'Filter by first auth method');
assert.strictEqual(
find(SELECTORS.selectedAuthMount).innerText.toLowerCase(),
topMount.label,
'selects top mount'
);
assert
.dom('[data-test-running-total-entity] p')
.includesText(`${formatNumber([topMount.entity_clients])}`, 'total entity clients is accurate');
assert
.dom('[data-test-running-total-nonentity] p')
.includesText(`${formatNumber([topMount.non_entity_clients])}`, 'total non-entity clients is accurate');
assert.dom(SELECTORS.attributionBlock).doesNotExist('Does not show attribution block');
await click('#namespace-search-select [data-test-selected-list-button="delete"]');
assert.ok(true, 'Remove namespace filter without first removing auth method filter');
assert.dom('[data-test-top-attribution]').includesText('Top namespace');
assert
.dom('[data-test-running-total-entity]')
.hasTextContaining(
`${formatNumber([response.total.entity_clients])}`,
'total entity clients is back to unfiltered value'
);
assert
.dom('[data-test-running-total-nonentity]')
.hasTextContaining(
`${formatNumber([formatNumber([response.total.non_entity_clients])])}`,
'total non-entity clients is back to unfiltered value'
);
assert
.dom('[data-test-attribution-clients]')
.hasTextContaining(
`${formatNumber([topNamespace.clients])}`,
'top attribution clients back to unfiltered value'
);
});
test('shows warning if upgrade happened within license period', async function (assert) {
assert.expect(3);
this.server.get('sys/version-history', function () {
return {
data: {
keys: ['1.9.0', '1.9.1', '1.9.2', '1.10.1'],
key_info: {
'1.9.0': {
previous_version: null,
timestamp_installed: formatRFC3339(subMonths(UPGRADE_DATE, 4)),
},
'1.9.1': {
previous_version: '1.9.0',
timestamp_installed: formatRFC3339(subMonths(UPGRADE_DATE, 3)),
},
'1.9.2': {
previous_version: '1.9.1',
timestamp_installed: formatRFC3339(subMonths(UPGRADE_DATE, 2)),
},
'1.10.1': {
previous_version: '1.9.2',
timestamp_installed: formatRFC3339(UPGRADE_DATE),
},
},
},
};
});
await visit('/vault/clients/dashboard');
assert.strictEqual(currentURL(), '/vault/clients/dashboard', 'clients/dashboard URL is correct');
assert.dom(SELECTORS.dashboardActiveTab).hasText('Dashboard', 'dashboard tab is active');
assert
.dom('[data-test-alert-banner="alert"]')
.hasTextContaining(
`Warning Vault was upgraded to 1.10.1 on Aug 1, 2022. We added monthly breakdowns and mount level attribution starting in 1.10, so keep that in mind when looking at the data. Learn more here.`
);
});
test('Shows empty if license start date is current month', async function (assert) {
// TODO cmb update to reflect new behavior
const licenseStart = STATIC_NOW;
const licenseEnd = addMonths(licenseStart, 12);
this.server.get('sys/license/status', function () {
return {
request_id: 'my-license-request-id',
data: {
autoloaded: {
license_id: 'my-license-id',
start_time: formatRFC3339(licenseStart),
expiration_time: formatRFC3339(licenseEnd),
},
},
};
});
await visit('/vault/clients/dashboard');
assert.strictEqual(currentURL(), '/vault/clients/dashboard', 'clients/dashboard URL is correct');
assert.dom(SELECTORS.emptyStateTitle).doesNotExist('No data for this billing period');
});
test('shows correct interface if no permissions on license', async function (assert) {
this.server.get('/sys/license/status', () => overrideResponse(403));
await visit('/vault/clients/dashboard');
assert.strictEqual(currentURL(), '/vault/clients/dashboard', 'clients/dashboard URL is correct');
assert.dom(SELECTORS.dashboardActiveTab).hasText('Dashboard', 'dashboard tab is active');
// Message changes depending on ent or OSS
assert.dom(SELECTORS.emptyStateTitle).exists('Empty state exists');
assert.dom(SELECTORS.monthDropdown).exists('Dropdown exists to select month');
assert.dom(SELECTORS.yearDropdown).exists('Dropdown exists to select year');
});
test('shows error template if permissions denied querying activity response with no data', async function (assert) {
this.server.get('sys/license/status', () => overrideResponse(403));
this.server.get('sys/version-history', () => overrideResponse(403));
this.server.get('sys/internal/counters/config', () => overrideResponse(403));
this.server.get('sys/internal/counters/activity', () => overrideResponse(403));
await visit('/vault/clients/dashboard');
assert.strictEqual(currentURL(), '/vault/clients/dashboard', 'clients/dashboard URL is correct');
assert
.dom(SELECTORS.emptyStateTitle)
.includesText('start date found', 'Empty state shows no billing start date');
await click(SELECTORS.monthDropdown);
await click(this.element.querySelector('[data-test-month-list] button:not([disabled])'));
await click(SELECTORS.yearDropdown);
await click(this.element.querySelector('[data-test-year-list] button:not([disabled])'));
await click(SELECTORS.dateDropdownSubmit);
assert
.dom(SELECTORS.emptyStateTitle)
.hasText('You are not authorized', 'Empty state displays not authorized message');
});
});