diff --git a/changelog/27796.txt b/changelog/27796.txt new file mode 100644 index 0000000000..7a1e7ebac3 --- /dev/null +++ b/changelog/27796.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: simplify the date range editing experience in the client counts dashboard. +``` \ No newline at end of file diff --git a/ui/app/components/calendar-widget.js b/ui/app/components/calendar-widget.js deleted file mode 100644 index 804c9af196..0000000000 --- a/ui/app/components/calendar-widget.js +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Component from '@glimmer/component'; -import { action } from '@ember/object'; -import { tracked } from '@glimmer/tracking'; -import { ARRAY_OF_MONTHS, parseAPITimestamp } from 'core/utils/date-formatters'; -import { addYears, isSameYear, subYears } from 'date-fns'; -import timestamp from 'core/utils/timestamp'; -/** - * @module CalendarWidget - * CalendarWidget component is used in the client counts dashboard to select a month/year to query the /activity endpoint. - * The component returns an object with selected date info, example: { dateType: 'endDate', monthIdx: 0, monthName: 'January', year: 2022 } - * - * @example - * ```js - * - * - * @param {string} startTimestamp - ISO timestamp string of the calendar widget's start time, displays in dropdown trigger - * @param {string} endTimestamp - ISO timestamp string for the calendar widget's end time, displays in dropdown trigger - * @param {function} selectMonth - callback function from parent - fires when selecting a month or clicking "Current billing period" - * /> - * ``` - */ -export default class CalendarWidget extends Component { - currentDate = timestamp.now(); - @tracked calendarDisplayDate = this.currentDate; // init to current date, updates when user clicks on calendar chevrons - @tracked showCalendar = false; - - // both date getters return a date object - get startDate() { - return parseAPITimestamp(this.args.startTimestamp); - } - get endDate() { - return parseAPITimestamp(this.args.endTimestamp); - } - get displayYear() { - return this.calendarDisplayDate.getFullYear(); - } - get disableFutureYear() { - return isSameYear(this.calendarDisplayDate, this.currentDate); - } - get disablePastYear() { - // calendar widget should only go as far back as the passed in start time - return isSameYear(this.calendarDisplayDate, this.startDate); - } - get widgetMonths() { - const startYear = this.startDate.getFullYear(); - const startMonthIdx = this.startDate.getMonth(); - return ARRAY_OF_MONTHS.map((month, index) => { - let readonly = false; - - // if widget is showing same year as @startTimestamp year, disable if month is before start month - if (startYear === this.displayYear && index < startMonthIdx) { - readonly = true; - } - - // if widget showing current year, disable if month is later than current month - if (this.displayYear === this.currentDate.getFullYear() && index > this.currentDate.getMonth()) { - readonly = true; - } - return { - index, - year: this.displayYear, - name: month, - readonly, - }; - }); - } - - @action - addYear() { - this.calendarDisplayDate = addYears(this.calendarDisplayDate, 1); - } - - @action - subYear() { - this.calendarDisplayDate = subYears(this.calendarDisplayDate, 1); - } - - @action - toggleShowCalendar() { - this.showCalendar = !this.showCalendar; - this.calendarDisplayDate = this.endDate; - } - - @action - handleDateShortcut(dropdown, { target }) { - this.args.selectMonth({ dateType: target.name }); // send clicked shortcut to parent callback - this.showCalendar = false; - dropdown.close(); - } - - @action - selectMonth(month, dropdown) { - const { index, year, name } = month; - this.toggleShowCalendar(); - this.args.selectMonth({ monthIdx: index, monthName: name, year, dateType: 'endDate' }); - dropdown.close(); - } -} diff --git a/ui/app/components/clients/date-range.hbs b/ui/app/components/clients/date-range.hbs new file mode 100644 index 0000000000..0d5ed63634 --- /dev/null +++ b/ui/app/components/clients/date-range.hbs @@ -0,0 +1,102 @@ +{{! + Copyright (c) HashiCorp, Inc. + SPDX-License-Identifier: BUSL-1.1 +~}} + +
+ + Date range + + +
+ {{#if (and @startTime @endTime)}} +

{{this.formattedDate @startTime}}

+

+

{{this.formattedDate @endTime}}

+ + {{else}} + + {{/if}} +
+ + {{#if this.showEditModal}} + + + Edit date range + + +

+ The start date will be used as the client counting start time and all clients in that month will be considered new. + {{#if this.version.isEnterprise}} + We recommend setting this date as your license or billing start date to get the most accurate new and total + client count estimations. These dates are only for querying data in storage. Editing the date range does not + change any license or billing configurations. + {{/if}} +

+
+
+ Start + +
+
+ End + +
+ +
+ {{#if this.validationError}} + {{this.validationError}} + {{/if}} + {{#if this.useDefaultDates}} + + Dashboard will use the default date range from the API. + + {{/if}} +
+ + + + +
+ {{/if}} +
\ No newline at end of file diff --git a/ui/app/components/clients/date-range.ts b/ui/app/components/clients/date-range.ts new file mode 100644 index 0000000000..4704a7c745 --- /dev/null +++ b/ui/app/components/clients/date-range.ts @@ -0,0 +1,125 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { action } from '@ember/object'; +import { service } from '@ember/service'; +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { formatDateObject } from 'core/utils/client-count-utils'; +import { parseAPITimestamp } from 'core/utils/date-formatters'; +import timestamp from 'core/utils/timestamp'; +import { format } from 'date-fns'; +import type VersionService from 'vault/services/version'; +import type { HTMLElementEvent } from 'forms'; + +interface OnChangeParams { + start_time: number | undefined; + end_time: number | undefined; +} +interface Args { + onChange: (callback: OnChangeParams) => void; + startTime: string; + endTime: string; +} +/** + * @module ClientsDateRange + * ClientsDateRange components are used to display the current date range and provide a modal interface for editing the date range. + * + * @example + * + * + * + * @param {function} onChange - callback when a new range is saved. + * @param {string} [startTime] - ISO string timestamp of the current start date + * @param {string} [endTime] - ISO string timestamp of the current end date + */ + +export default class ClientsDateRangeComponent extends Component { + @service declare readonly version: VersionService; + + @tracked showEditModal = false; + @tracked startDate = ''; // format yyyy-MM + @tracked endDate = ''; // format yyyy-MM + currentMonth = format(timestamp.now(), 'yyyy-MM'); + + constructor(owner: unknown, args: Args) { + super(owner, args); + this.setTrackedFromArgs(); + } + + setTrackedFromArgs() { + if (this.args.startTime) { + this.startDate = parseAPITimestamp(this.args.startTime, 'yyyy-MM') as string; + } + if (this.args.endTime) { + this.endDate = parseAPITimestamp(this.args.endTime, 'yyyy-MM') as string; + } + } + + formattedDate = (isoTimestamp: string) => { + return parseAPITimestamp(isoTimestamp, 'MMMM yyyy'); + }; + + get useDefaultDates() { + return !this.startDate && !this.endDate; + } + + get validationError() { + if (this.useDefaultDates) { + // this means we want to reset, which is fine + return null; + } + if (!this.startDate || !this.endDate) { + return 'You must supply both start and end dates.'; + } + if (this.startDate > this.endDate) { + return 'Start date must be before end date.'; + } + return null; + } + + @action onClose() { + // since the component never gets torn down, we have to manually re-set this on close + this.setTrackedFromArgs(); + this.showEditModal = false; + } + + @action resetDates() { + this.startDate = ''; + this.endDate = ''; + } + + @action updateDate(evt: HTMLElementEvent) { + const { name, value } = evt.target; + if (name === 'end') { + this.endDate = value; + } else { + this.startDate = value; + } + } + + @action handleSave() { + if (this.validationError) return; + const params: OnChangeParams = { + start_time: undefined, + end_time: undefined, + }; + if (this.startDate) { + const [year, month] = this.startDate.split('-'); + if (year && month) { + params.start_time = formatDateObject({ monthIdx: parseInt(month) - 1, year: parseInt(year) }, false); + } + } + if (this.endDate) { + const [year, month] = this.endDate.split('-'); + if (year && month) { + params.end_time = formatDateObject({ monthIdx: parseInt(month) - 1, year: parseInt(year) }, true); + } + } + + this.args.onChange(params); + this.onClose(); + } +} diff --git a/ui/app/components/clients/page/counts.hbs b/ui/app/components/clients/page/counts.hbs index 91c401bda7..bb369ced1f 100644 --- a/ui/app/components/clients/page/counts.hbs +++ b/ui/app/components/clients/page/counts.hbs @@ -18,35 +18,12 @@ Date queries are sent in UTC.

- - {{this.versionText.label}} - - -
- {{#if this.formattedStartDate}} -

{{this.formattedStartDate}}

- - {{else}} - - {{/if}} -
- - - {{this.versionText.description}} - + {{#if (eq @activity.id "no-data")}} @@ -76,11 +53,6 @@ - {{#if (or @namespace this.namespaces)}} {{/if}} {{/if}} - - -{{#if this.showBillingStartModal}} - - - Edit start month - - -

- {{this.versionText.description}} -

-

{{this.versionText.label}}

- -
- - - -
-{{/if}} \ No newline at end of file + \ No newline at end of file diff --git a/ui/app/components/clients/page/counts.ts b/ui/app/components/clients/page/counts.ts index 265c90da17..b2cf003ddd 100644 --- a/ui/app/components/clients/page/counts.ts +++ b/ui/app/components/clients/page/counts.ts @@ -6,10 +6,9 @@ import Component from '@glimmer/component'; import { service } from '@ember/service'; import { action } from '@ember/object'; -import { fromUnixTime, getUnixTime, isSameMonth, isAfter } from 'date-fns'; +import { fromUnixTime, isSameMonth, isAfter } from 'date-fns'; import { parseAPITimestamp } from 'core/utils/date-formatters'; -import { filterVersionHistory, formatDateObject } from 'core/utils/client-count-utils'; -import timestamp from 'core/utils/timestamp'; +import { filterVersionHistory } from 'core/utils/client-count-utils'; import type AdapterError from '@ember-data/adapter'; import type FlagsService from 'vault/services/flags'; @@ -94,17 +93,11 @@ export default class ClientsCountsPageComponent extends Component { get versionText() { return this.version.isEnterprise ? { - label: 'Billing start month', - description: - 'This date comes from your license, and defines when client counting starts. Without this starting point, the data shown is not reliable.', title: 'No billing start date found', message: 'In order to get the most from this data, please enter your billing period start month. This will ensure that the resulting data is accurate.', } : { - label: 'Client counting start date', - description: - 'This date is when client counting starts. Without this starting point, the data shown is not reliable.', title: 'No start date found', message: 'In order to get the most from this data, please enter a start month above. Vault will calculate new clients starting from that month.', @@ -174,25 +167,8 @@ export default class ClientsCountsPageComponent extends Component { } @action - onDateChange(dateObject: { dateType: string; monthIdx: number; year: number }) { - const { dateType, monthIdx, year } = dateObject; - const { config } = this.args; - const currentTimestamp = getUnixTime(timestamp.now()); - - // converts the selectedDate to unix timestamp for activity query - const selectedDate = formatDateObject({ monthIdx, year }, dateType === 'endDate'); - - if (dateType !== 'cancel') { - const start_time = { - reset: getUnixTime(config?.billingStartTimestamp) || null, // clicked 'Current billing period' in calendar widget -> resets to billing start date - currentMonth: currentTimestamp, // clicked 'Current month' from calendar widget -> defaults to currentTimestamp - startDate: selectedDate, // from "Edit billing start" modal - }[dateType]; - // endDate type is selection from calendar widget - const end_time = dateType === 'endDate' ? selectedDate : currentTimestamp; // defaults to currentTimestamp - const params = start_time !== undefined ? { start_time, end_time } : { end_time }; - this.args.onFilterChange(params); - } + onDateChange(params: { start_time: number | undefined; end_time: number | undefined }) { + this.args.onFilterChange(params); } @action diff --git a/ui/app/components/date-dropdown.js b/ui/app/components/date-dropdown.js deleted file mode 100644 index 7fee2b4551..0000000000 --- a/ui/app/components/date-dropdown.js +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import Component from '@glimmer/component'; -import { action } from '@ember/object'; -import { tracked } from '@glimmer/tracking'; -import { ARRAY_OF_MONTHS } from 'core/utils/date-formatters'; -import timestamp from 'core/utils/timestamp'; -/** - * @module DateDropdown - * DateDropdown components are used to display a dropdown of months and years to handle date selection. Future dates are disabled (current month and year are selectable). - * The component returns an object with selected date info, example: { dateType: 'start', monthIdx: 0, monthName: 'January', year: 2022 } - * - * @example - * ```js - * - * ``` - * @param {function} handleSubmit - callback function from parent that the date picker triggers on submit - * @param {string} [dateType] - optional argument to give the selected month/year a type - * @param {string} [submitText] - optional argument to change submit button text - * @param {function} [validateDate] - parent function to validate date selection, receives date object and returns an error message that's passed to the inline alert - */ -export default class DateDropdown extends Component { - currentDate = timestamp.now(); - currentYear = this.currentDate.getFullYear(); // integer of year - currentMonthIdx = this.currentDate.getMonth(); // integer of month, 0 indexed - dropdownMonths = ARRAY_OF_MONTHS.map((m, i) => ({ name: m, index: i })); - dropdownYears = Array.from({ length: 5 }, (item, i) => this.currentYear - i); - - @tracked maxMonthIdx = 11; // disables months with index greater than this number, initially all months are selectable - @tracked disabledYear = null; // year as integer if current year should be disabled - @tracked selectedMonth = null; - @tracked selectedYear = null; - @tracked invalidDate = null; - - @action - selectMonth(month, dropdown) { - this.selectedMonth = month; - // disable current year if selected month is later than current month - this.disabledYear = month.index > this.currentMonthIdx ? this.currentYear : null; - dropdown.close(); - } - - @action - selectYear(year, dropdown) { - this.selectedYear = year; - // disable months after current month if selected year is current year - this.maxMonthIdx = year === this.currentYear ? this.currentMonthIdx : 11; - dropdown.close(); - } - - @action - handleSubmit() { - if (this.args.validateDate) { - this.invalidDate = null; - this.invalidDate = this.args.validateDate(new Date(this.selectedYear, this.selectedMonth.index)); - if (this.invalidDate) return; - } - const { index, name } = this.selectedMonth; - this.args.handleSubmit({ - monthIdx: index, - monthName: name, - year: this.selectedYear, - dateType: this.args.dateType, - }); - this.resetDropdown(); - } - - resetDropdown() { - this.maxMonthIdx = 11; - this.disabledYear = null; - this.selectedMonth = null; - this.selectedYear = null; - this.invalidDate = null; - } -} diff --git a/ui/app/routes/vault/cluster/clients/counts.ts b/ui/app/routes/vault/cluster/clients/counts.ts index 3c87c2922e..b2322970db 100644 --- a/ui/app/routes/vault/cluster/clients/counts.ts +++ b/ui/app/routes/vault/cluster/clients/counts.ts @@ -13,8 +13,6 @@ import type StoreService from 'vault/services/store'; import type VersionService from 'vault/services/version'; import type { ModelFrom } from 'vault/vault/route'; import type ClientsRoute from '../clients'; -import type ClientsActivityModel from 'vault/models/clients/activity'; -import type ClientsConfigModel from 'vault/models/clients/config'; import type ClientsCountsController from 'vault/controllers/vault/cluster/clients/counts'; import { setStartTimeQuery } from 'core/utils/client-count-utils'; diff --git a/ui/app/styles/components/clients-date-range.scss b/ui/app/styles/components/clients-date-range.scss new file mode 100644 index 0000000000..e908211109 --- /dev/null +++ b/ui/app/styles/components/clients-date-range.scss @@ -0,0 +1,14 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +.clients-date-range-display { + display: flex; + align-items: bottom; + > * { + margin-right: $spacing-8; + vertical-align: bottom; + align-self: end; + } +} diff --git a/ui/app/styles/core.scss b/ui/app/styles/core.scss index 84882a7bd3..2d020970b7 100644 --- a/ui/app/styles/core.scss +++ b/ui/app/styles/core.scss @@ -59,6 +59,7 @@ @import './components/box-label'; @import './components/calendar-widget'; @import './components/chart-container'; +@import './components/clients-date-range'; @import './components/cluster-banners'; @import './components/codemirror'; @import './components/console-ui-panel'; diff --git a/ui/app/templates/components/date-dropdown.hbs b/ui/app/templates/components/date-dropdown.hbs deleted file mode 100644 index d154886f6a..0000000000 --- a/ui/app/templates/components/date-dropdown.hbs +++ /dev/null @@ -1,38 +0,0 @@ -{{! - Copyright (c) HashiCorp, Inc. - SPDX-License-Identifier: BUSL-1.1 -~}} - - - - - {{#each this.dropdownMonths as |month|}} - - {{/each}} - - - - {{#each this.dropdownYears as |year|}} - - {{/each}} - - - -{{#if this.invalidDate}} - -{{/if}} \ No newline at end of file diff --git a/ui/tests/acceptance/clients/counts-test.js b/ui/tests/acceptance/clients/counts-test.js index f598b75cf6..4c283f4b83 100644 --- a/ui/tests/acceptance/clients/counts-test.js +++ b/ui/tests/acceptance/clients/counts-test.js @@ -8,7 +8,7 @@ import { setupApplicationTest } from 'ember-qunit'; import { setupMirage } from 'ember-cli-mirage/test-support'; import clientsHandler, { STATIC_NOW } from 'vault/mirage/handlers/clients'; import sinon from 'sinon'; -import { visit, click, currentURL } from '@ember/test-helpers'; +import { visit, click, currentURL, fillIn } from '@ember/test-helpers'; import authPage from 'vault/tests/pages/auth'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { CLIENT_COUNT } from 'vault/tests/helpers/clients/client-count-selectors'; @@ -42,14 +42,20 @@ module('Acceptance | clients | counts', function (hooks) { test('it should persist filter query params between child routes', async function (assert) { await visit('/vault/clients/counts/overview'); - await click(CLIENT_COUNT.rangeDropdown); - await click(CLIENT_COUNT.currentBillingPeriod); - const timeQueryRegex = /end_time=\d+&start_time=\d+/g; - assert.ok(currentURL().match(timeQueryRegex).length, 'Start and end times added as query params'); + await click(CLIENT_COUNT.dateRange.edit); + await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2020-03'); + await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2022-02'); + await click(GENERAL.saveButton); + assert.strictEqual( + currentURL(), + '/vault/clients/counts/overview?end_time=1706659200&start_time=1643673600', + 'Start and end times added as query params' + ); await click(GENERAL.tab('token')); - assert.ok( - currentURL().match(timeQueryRegex).length, + assert.strictEqual( + currentURL(), + '/vault/clients/counts/token?end_time=1706659200&start_time=1643673600', 'Start and end times persist through child route change' ); diff --git a/ui/tests/acceptance/clients/counts/overview-test.js b/ui/tests/acceptance/clients/counts/overview-test.js index 2251f64db8..6e5ba2dedc 100644 --- a/ui/tests/acceptance/clients/counts/overview-test.js +++ b/ui/tests/acceptance/clients/counts/overview-test.js @@ -9,9 +9,8 @@ import { setupMirage } from 'ember-cli-mirage/test-support'; import clientsHandler, { STATIC_NOW, LICENSE_START, UPGRADE_DATE } from 'vault/mirage/handlers/clients'; import syncHandler from 'vault/mirage/handlers/sync'; import sinon from 'sinon'; -import { visit, click, findAll, settled } from '@ember/test-helpers'; +import { visit, click, findAll, settled, fillIn } from '@ember/test-helpers'; import authPage from 'vault/tests/pages/auth'; -import { ARRAY_OF_MONTHS } from 'core/utils/date-formatters'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { CHARTS, CLIENT_COUNT } from 'vault/tests/helpers/clients/client-count-selectors'; import { create } from 'ember-cli-page-object'; @@ -19,6 +18,7 @@ import { clickTrigger } from 'ember-power-select/test-support/helpers'; import { formatNumber } from 'core/helpers/format-number'; import timestamp from 'core/utils/timestamp'; import ss from 'vault/tests/pages/components/search-select'; +import { format } from 'date-fns'; const searchSelect = create(ss); @@ -36,11 +36,11 @@ module('Acceptance | clients | overview', function (hooks) { test('it should render charts', async function (assert) { assert - .dom(CLIENT_COUNT.counts.startMonth) + .dom(CLIENT_COUNT.dateRange.dateDisplay('start')) .hasText('July 2023', 'billing start month is correctly parsed from license'); assert - .dom(CLIENT_COUNT.rangeDropdown) - .hasText('Jul 2023 - Jan 2024', 'Date range shows dates correctly parsed activity response'); + .dom(CLIENT_COUNT.dateRange.dateDisplay('end')) + .hasText('January 2024', 'billing start month is correctly parsed from license'); assert.dom(CLIENT_COUNT.attributionBlock).exists('Shows attribution area'); assert .dom(CHARTS.container('Vault client counts')) @@ -53,12 +53,13 @@ module('Acceptance | clients | overview', function (hooks) { test('it should update charts when querying date ranges', async function (assert) { // query for single, historical month with no new counts (July 2023) - await click(CLIENT_COUNT.rangeDropdown); - await click(CLIENT_COUNT.calendarWidget.customEndMonth); - await click(CLIENT_COUNT.calendarWidget.previousYear); + const licenseStartMonth = format(LICENSE_START, 'yyyy-MM'); + const upgradeMonth = format(UPGRADE_DATE, 'yyyy-MM'); + await click(CLIENT_COUNT.dateRange.edit); + await fillIn(CLIENT_COUNT.dateRange.editDate('start'), licenseStartMonth); + await fillIn(CLIENT_COUNT.dateRange.editDate('end'), licenseStartMonth); - const month = ARRAY_OF_MONTHS[LICENSE_START.getMonth()]; - await click(CLIENT_COUNT.calendarWidget.calendarMonth(month)); + await click(GENERAL.saveButton); assert .dom(CLIENT_COUNT.usageStats('Vault client counts')) .doesNotExist('running total single month stat boxes do not show'); @@ -75,16 +76,15 @@ module('Acceptance | clients | overview', function (hooks) { assert.dom(CHARTS.container('total-clients')).exists('total client attribution chart shows'); // reset to billing period - await click(CLIENT_COUNT.rangeDropdown); - await click(CLIENT_COUNT.currentBillingPeriod); + await click(CLIENT_COUNT.dateRange.edit); + await click(CLIENT_COUNT.dateRange.reset); + await click(GENERAL.saveButton); + + // change to start on month/year of upgrade to 1.10 + await click(CLIENT_COUNT.dateRange.edit); + await fillIn(CLIENT_COUNT.dateRange.editDate('start'), upgradeMonth); + await click(GENERAL.saveButton); - // change billing start to month/year of upgrade to 1.10 - await click(CLIENT_COUNT.counts.startEdit); - await click(CLIENT_COUNT.monthDropdown); - await click(CLIENT_COUNT.dateDropdown.selectMonth(ARRAY_OF_MONTHS[UPGRADE_DATE.getMonth()])); - await click(CLIENT_COUNT.yearDropdown); - await click(CLIENT_COUNT.dateDropdown.selectYear(UPGRADE_DATE.getFullYear())); - await click(CLIENT_COUNT.dateDropdown.submit); assert.dom(CLIENT_COUNT.attributionBlock).exists('Shows attribution area'); assert .dom(CHARTS.container('Vault client counts')) @@ -95,11 +95,11 @@ module('Acceptance | clients | overview', function (hooks) { assert.strictEqual(findAll(CHARTS.plotPoint).length, 5, 'line chart plots 5 points to match query'); // query for single, historical month (upgrade month) - await click(CLIENT_COUNT.rangeDropdown); - await click(CLIENT_COUNT.calendarWidget.customEndMonth); - assert.dom(CLIENT_COUNT.calendarWidget.displayYear).hasText('2024'); - await click(CLIENT_COUNT.calendarWidget.previousYear); - await click(CLIENT_COUNT.calendarWidget.calendarMonth('September')); + await click(CLIENT_COUNT.dateRange.edit); + await fillIn(CLIENT_COUNT.dateRange.editDate('start'), upgradeMonth); + await fillIn(CLIENT_COUNT.dateRange.editDate('end'), upgradeMonth); + await click(GENERAL.saveButton); + assert .dom(CLIENT_COUNT.usageStats('Vault client counts')) .exists('running total single month usage stats show'); @@ -111,9 +111,10 @@ module('Acceptance | clients | overview', function (hooks) { assert.dom(CHARTS.container('total-clients')).exists('total client attribution chart shows'); // query historical date range (from September 2023 to December 2023) - await click(CLIENT_COUNT.rangeDropdown); - await click(CLIENT_COUNT.calendarWidget.customEndMonth); - await click(CLIENT_COUNT.calendarWidget.calendarMonth('December')); + await click(CLIENT_COUNT.dateRange.edit); + await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2023-09'); + await fillIn(CLIENT_COUNT.dateRange.editDate('end'), '2023-12'); + await click(GENERAL.saveButton); assert.dom(CLIENT_COUNT.attributionBlock).exists('Shows attribution area'); assert @@ -126,15 +127,15 @@ module('Acceptance | clients | overview', function (hooks) { .hasText('12/23', 'x-axis labels end with queried end month'); // reset to billing period - await click(CLIENT_COUNT.rangeDropdown); - await click(CLIENT_COUNT.currentBillingPeriod); + await click(CLIENT_COUNT.dateRange.edit); + await click(CLIENT_COUNT.dateRange.reset); + await click(GENERAL.saveButton); + // query month older than count start date - await click(CLIENT_COUNT.counts.startEdit); - await click(CLIENT_COUNT.monthDropdown); - await click(CLIENT_COUNT.dateDropdown.selectMonth(ARRAY_OF_MONTHS[LICENSE_START.getMonth()])); - await click(CLIENT_COUNT.yearDropdown); - await click(CLIENT_COUNT.dateDropdown.selectYear(LICENSE_START.getFullYear() - 3)); - await click(CLIENT_COUNT.dateDropdown.submit); + await click(CLIENT_COUNT.dateRange.edit); + await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2020-07'); + await click(GENERAL.saveButton); + assert .dom(CLIENT_COUNT.counts.startDiscrepancy) .hasTextContaining( diff --git a/ui/tests/helpers/clients/client-count-helpers.js b/ui/tests/helpers/clients/client-count-helpers.js index d433c9f095..a4d38a723a 100644 --- a/ui/tests/helpers/clients/client-count-helpers.js +++ b/ui/tests/helpers/clients/client-count-helpers.js @@ -3,18 +3,8 @@ * SPDX-License-Identifier: BUSL-1.1 */ -import { click, findAll } from '@ember/test-helpers'; -import { CLIENT_COUNT, CHARTS } from './client-count-selectors'; - -export async function dateDropdownSelect(month, year) { - const { dateDropdown, counts } = CLIENT_COUNT; - await click(counts.startEdit); - await click(dateDropdown.toggleMonth); - await click(dateDropdown.selectMonth(month)); - await click(dateDropdown.toggleYear); - await click(dateDropdown.selectYear(year)); - await click(dateDropdown.submit); -} +import { findAll } from '@ember/test-helpers'; +import { CHARTS } from './client-count-selectors'; export function assertBarChart(assert, chartName, byMonthData, isStacked = false) { // assertion count is byMonthData.length, plus 2 diff --git a/ui/tests/helpers/clients/client-count-selectors.ts b/ui/tests/helpers/clients/client-count-selectors.ts index 4d329ff619..53691cfabf 100644 --- a/ui/tests/helpers/clients/client-count-selectors.ts +++ b/ui/tests/helpers/clients/client-count-selectors.ts @@ -6,44 +6,28 @@ // TODO: separate nested into distinct exported consts export const CLIENT_COUNT = { counts: { - startLabel: '[data-test-counts-start-label]', description: '[data-test-counts-description]', - startMonth: '[data-test-counts-start-month]', - startEdit: '[data-test-counts-start-edit]', - startDropdown: '[data-test-counts-start-dropdown]', configDisabled: '[data-test-counts-disabled]', namespaces: '[data-test-counts-namespaces]', mountPaths: '[data-test-counts-auth-mounts]', startDiscrepancy: '[data-test-counts-start-discrepancy]', }, + dateRange: { + dateDisplay: (name: string) => (name ? `[data-test-date-range="${name}"]` : '[data-test-date-range]'), + set: '[data-test-set-date-range]', + edit: '[data-test-date-range-edit]', + editModal: '[data-test-date-range-edit-modal]', + editDate: (name: string) => `[data-test-date-edit="${name}"]`, + reset: '[data-test-date-edit="reset"]', + defaultRangeAlert: '[data-test-range-default-alert]', + validation: '[data-test-date-range-validation]', + }, statText: (label: string) => `[data-test-stat-text="${label}"]`, statTextValue: (label: string) => label ? `[data-test-stat-text="${label}"] .stat-value` : '[data-test-stat-text]', usageStats: (title: string) => `[data-test-usage-stats="${title}"]`, - dateDisplay: '[data-test-date-display]', attributionBlock: '[data-test-clients-attribution]', filterBar: '[data-test-clients-filter-bar]', - rangeDropdown: '[data-test-calendar-widget-trigger]', - monthDropdown: '[data-test-toggle-month]', - yearDropdown: '[data-test-toggle-year]', - currentBillingPeriod: '[data-test-current-billing-period]', - dateDropdown: { - toggleMonth: '[data-test-toggle-month]', - toggleYear: '[data-test-toggle-year]', - selectMonth: (month: string) => `[data-test-dropdown-month="${month}"]`, - selectYear: (year: string) => `[data-test-dropdown-year="${year}"]`, - submit: '[data-test-date-dropdown-submit]', - }, - calendarWidget: { - trigger: '[data-test-calendar-widget-trigger]', - currentMonth: '[data-test-current-month]', - currentBillingPeriod: '[data-test-current-billing-period]', - customEndMonth: '[data-test-show-calendar]', - previousYear: '[data-test-previous-year]', - nextYear: '[data-test-next-year]', - displayYear: '[data-test-display-year]', - calendarMonth: (month: string) => `[data-test-calendar-month="${month}"]`, - }, selectedAuthMount: 'div#mounts-search-select [data-test-selected-option] div', selectedNs: 'div#namespace-search-select [data-test-selected-option] div', upgradeWarning: '[data-test-clients-upgrade-warning]', diff --git a/ui/tests/integration/components/calendar-widget-test.js b/ui/tests/integration/components/calendar-widget-test.js deleted file mode 100644 index 46a1a19061..0000000000 --- a/ui/tests/integration/components/calendar-widget-test.js +++ /dev/null @@ -1,240 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { module, test } from 'qunit'; -import { setupRenderingTest } from 'ember-qunit'; -import { render, click } from '@ember/test-helpers'; -import sinon from 'sinon'; -import hbs from 'htmlbars-inline-precompile'; -import calendarDropdown from 'vault/tests/pages/components/calendar-widget'; -import { ARRAY_OF_MONTHS } from 'core/utils/date-formatters'; -import { subMonths, subYears } from 'date-fns'; -import timestamp from 'core/utils/timestamp'; - -module('Integration | Component | calendar-widget', function (hooks) { - setupRenderingTest(hooks); - - hooks.beforeEach(function () { - sinon.replace(timestamp, 'now', sinon.fake.returns(new Date('2018-04-03T14:15:30'))); - const CURRENT_DATE = timestamp.now(); - this.set('currentDate', CURRENT_DATE); - this.set('calendarStartDate', subMonths(CURRENT_DATE, 12)); - this.set('calendarEndDate', CURRENT_DATE); - this.set('startTimestamp', subMonths(CURRENT_DATE, 12).toISOString()); - this.set('endTimestamp', CURRENT_DATE.toISOString()); - this.set('handleClientActivityQuery', sinon.spy()); - }); - - test('it renders and disables correct months when start date is 12 months ago', async function (assert) { - assert.expect(14); - await render(hbs` - - `); - assert - .dom('[data-test-calendar-widget-trigger]') - .hasText(`Apr 2017 - Apr 2018`, 'renders and formats start and end dates'); - await calendarDropdown.openCalendar(); - assert.ok(calendarDropdown.showsCalendar, 'renders the calendar component'); - // assert months in current year are disabled/enabled correctly - const enabledMonths = ['January', 'February', 'March', 'April']; - ARRAY_OF_MONTHS.forEach(function (month) { - if (enabledMonths.includes(month)) { - assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`); - } else { - assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is disabled`); - } - }); - }); - - test('it renders and disables months before start timestamp', async function (assert) { - await render(hbs` - - `); - - await calendarDropdown.openCalendar(); - assert.dom('[data-test-next-year]').isDisabled('Future year is disabled'); - await calendarDropdown.clickPreviousYear(); - assert - .dom('[data-test-display-year]') - .hasText(`${subYears(this.currentDate, 1).getFullYear()}`, 'shows the previous year'); - assert.dom('[data-test-previous-year]').isDisabled('disables previous year'); - - // assert months in previous year are disabled/enabled correctly - const disabledMonths = ['January', 'February', 'March']; - ARRAY_OF_MONTHS.forEach(function (month) { - if (disabledMonths.includes(month)) { - assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is disabled`); - } else { - assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`); - } - }); - }); - - test('it calls parent callback with correct arg when clicking "Current billing period"', async function (assert) { - await render(hbs` - - `); - await calendarDropdown.menuToggle(); - await calendarDropdown.clickCurrentBillingPeriod(); - assert.propEqual( - this.handleClientActivityQuery.args[0][0], - { dateType: 'reset' }, - 'it calls parent function with reset dateType' - ); - }); - - test('it calls parent callback with correct arg when clicking "Current month"', async function (assert) { - await render(hbs` - - `); - await calendarDropdown.menuToggle(); - await calendarDropdown.clickCurrentMonth(); - assert.propEqual( - this.handleClientActivityQuery.args[0][0], - { dateType: 'currentMonth' }, - 'it calls parent function with currentMoth dateType' - ); - }); - - test('it calls parent callback with correct arg when selecting a month', async function (assert) { - await render(hbs` - - `); - await calendarDropdown.openCalendar(); - await click(`[data-test-calendar-month="April"`); - assert.propEqual( - this.handleClientActivityQuery.lastCall.lastArg, - { - dateType: 'endDate', - monthIdx: 3, - monthName: 'April', - year: 2018, - }, - 'it calls parent function with end date (current) month/year' - ); - - await calendarDropdown.openCalendar(); - await calendarDropdown.clickPreviousYear(); - await click(`[data-test-calendar-month="May"]`); - assert.propEqual( - this.handleClientActivityQuery.lastCall.lastArg, - { - dateType: 'endDate', - monthIdx: 4, - monthName: 'May', - year: 2017, - }, - 'it calls parent function with selected start date month/year' - ); - }); - - test('it disables correct months when start date 6 months ago', async function (assert) { - this.set('calendarStartDate', subMonths(this.currentDate, 6)); // Nov 3, 2017 - this.set('startTimestamp', subMonths(this.currentDate, 6).toISOString()); - await render(hbs` - - `); - - await calendarDropdown.openCalendar(); - assert.dom('[data-test-next-year]').isDisabled('Future year is disabled'); - - // Check start year disables correct months - await calendarDropdown.clickPreviousYear(); - assert.dom('[data-test-previous-year]').isDisabled('previous year is disabled'); - const prevYearEnabled = ['October', 'November', 'December']; - ARRAY_OF_MONTHS.forEach(function (month) { - if (prevYearEnabled.includes(month)) { - assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`); - } else { - assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is read only`); - } - }); - - // Check end year disables correct months - await click('[data-test-next-year]'); - const currYearEnabled = ['January', 'February', 'March', 'April']; - ARRAY_OF_MONTHS.forEach(function (month) { - if (currYearEnabled.includes(month)) { - assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`); - } else { - assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is disabled`); - } - }); - }); - - test('it disables correct months when start date 36 months ago', async function (assert) { - this.set('calendarStartDate', subMonths(this.currentDate, 36)); // April 3 2015 - this.set('startTimestamp', subMonths(this.currentDate, 36).toISOString()); - await render(hbs` - - `); - - await calendarDropdown.openCalendar(); - assert.dom('[data-test-next-year]').isDisabled('Future year is disabled'); - for (const year of [2017, 2016, 2015]) { - await calendarDropdown.clickPreviousYear(); - assert.dom('[data-test-display-year]').hasText(year.toString()); - } - - assert.dom('[data-test-previous-year]').isDisabled('previous year is disabled'); - assert.dom('[data-test-next-year]').isEnabled('next year is enabled'); - - const disabledMonths = ['January', 'February', 'March']; - ARRAY_OF_MONTHS.forEach(function (month) { - if (disabledMonths.includes(month)) { - assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is disabled`); - } else { - assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`); - } - }); - - await click('[data-test-next-year]'); - ARRAY_OF_MONTHS.forEach(function (month) { - assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled for 2016`); - }); - await click('[data-test-next-year]'); - ARRAY_OF_MONTHS.forEach(function (month) { - assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled for 2017`); - }); - await click('[data-test-next-year]'); - - const enabledMonths = ['January', 'February', 'March', 'April']; - ARRAY_OF_MONTHS.forEach(function (month) { - if (enabledMonths.includes(month)) { - assert.dom(`[data-test-calendar-month="${month}"]`).isNotDisabled(`${month} is enabled`); - } else { - assert.dom(`[data-test-calendar-month="${month}"]`).isDisabled(`${month} is disabled`); - } - }); - }); -}); diff --git a/ui/tests/integration/components/clients/date-range-test.js b/ui/tests/integration/components/clients/date-range-test.js new file mode 100644 index 0000000000..beec2fe491 --- /dev/null +++ b/ui/tests/integration/components/clients/date-range-test.js @@ -0,0 +1,106 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'vault/tests/helpers'; +import { click, fillIn, render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import Sinon from 'sinon'; +import timestamp from 'core/utils/timestamp'; +import { GENERAL } from 'vault/tests/helpers/general-selectors'; +import { CLIENT_COUNT } from 'vault/tests/helpers/clients/client-count-selectors'; + +const DATE_RANGE = CLIENT_COUNT.dateRange; +module('Integration | Component | clients/date-range', function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function () { + Sinon.replace(timestamp, 'now', Sinon.fake.returns(new Date('2018-04-03T14:15:30'))); + this.now = timestamp.now(); + this.startTime = '2018-01-01T14:15:30'; + this.endTime = '2019-01-31T14:15:30'; + this.onChange = Sinon.spy(); + this.renderComponent = async () => { + await render( + hbs`` + ); + }; + }); + + test('it renders prompt to set dates if no start time', async function (assert) { + this.startTime = undefined; + await this.renderComponent(); + + assert.dom(DATE_RANGE.set).exists(); + + await click(DATE_RANGE.set); + assert.dom(DATE_RANGE.editModal).exists(); + assert.dom(DATE_RANGE.editDate('start')).hasValue(''); + await fillIn(DATE_RANGE.editDate('start'), '2018-01'); + await fillIn(DATE_RANGE.editDate('end'), '2019-01'); + await click(GENERAL.saveButton); + assert.deepEqual(this.onChange.args[0], [ + { + end_time: 1548892800, + start_time: 1514764800, + }, + ]); + assert.dom(DATE_RANGE.editModal).doesNotExist('closes modal'); + }); + + test('it renders the date range passed and can reset it', async function (assert) { + await this.renderComponent(); + + assert.dom(DATE_RANGE.dateDisplay('start')).hasText('January 2018'); + assert.dom(DATE_RANGE.dateDisplay('end')).hasText('January 2019'); + + await click(DATE_RANGE.edit); + assert.dom(DATE_RANGE.editModal).exists(); + assert.dom(DATE_RANGE.editDate('start')).hasValue('2018-01'); + assert.dom(DATE_RANGE.editDate('end')).hasValue('2019-01'); + assert.dom(DATE_RANGE.defaultRangeAlert).doesNotExist(); + + await click(DATE_RANGE.editDate('reset')); + assert.dom(DATE_RANGE.editDate('start')).hasValue(''); + assert.dom(DATE_RANGE.editDate('end')).hasValue(''); + assert.dom(DATE_RANGE.defaultRangeAlert).exists(); + await click(GENERAL.saveButton); + assert.deepEqual(this.onChange.args[0], [{ start_time: undefined, end_time: undefined }]); + }); + + test('it does not trigger onChange if date range invalid', async function (assert) { + await this.renderComponent(); + + await click(DATE_RANGE.edit); + await click(DATE_RANGE.editDate('reset')); + await fillIn(DATE_RANGE.editDate('end'), '2017-05'); + assert.dom(DATE_RANGE.validation).hasText('You must supply both start and end dates.'); + await click(GENERAL.saveButton); + assert.false(this.onChange.called); + + await fillIn(DATE_RANGE.editDate('start'), '2018-01'); + assert.dom(DATE_RANGE.validation).hasText('Start date must be before end date.'); + await click(GENERAL.saveButton); + assert.false(this.onChange.called); + + await click(GENERAL.cancelButton); + assert.false(this.onChange.called); + assert.dom(DATE_RANGE.editModal).doesNotExist(); + }); + + test('it resets the tracked values on close', async function (assert) { + await this.renderComponent(); + + await click(DATE_RANGE.edit); + await click(DATE_RANGE.editDate('reset')); + assert.dom(DATE_RANGE.editDate('start')).hasValue(''); + assert.dom(DATE_RANGE.editDate('end')).hasValue(''); + await click(GENERAL.cancelButton); + + await click(DATE_RANGE.edit); + assert.dom(DATE_RANGE.editDate('start')).hasValue('2018-01'); + assert.dom(DATE_RANGE.editDate('end')).hasValue('2019-01'); + }); +}); diff --git a/ui/tests/integration/components/clients/page/counts-test.js b/ui/tests/integration/components/clients/page/counts-test.js index d4a3cca34c..4577d695c6 100644 --- a/ui/tests/integration/components/clients/page/counts-test.js +++ b/ui/tests/integration/components/clients/page/counts-test.js @@ -6,13 +6,12 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; import { setupMirage } from 'ember-cli-mirage/test-support'; -import { render, click, settled, findAll } from '@ember/test-helpers'; +import { render, click, findAll, fillIn } from '@ember/test-helpers'; import hbs from 'htmlbars-inline-precompile'; import clientsHandler, { LICENSE_START, STATIC_NOW } from 'vault/mirage/handlers/clients'; import { getUnixTime } from 'date-fns'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { CLIENT_COUNT } from 'vault/tests/helpers/clients/client-count-selectors'; -import { dateDropdownSelect } from 'vault/tests/helpers/clients/client-count-helpers'; import { selectChoose } from 'ember-power-select/test-support'; import timestamp from 'core/utils/timestamp'; import sinon from 'sinon'; @@ -57,38 +56,11 @@ module('Integration | Component | clients | Page::Counts', function (hooks) { `); }); - test('it should render start date label and description based on version', async function (assert) { - const versionService = this.owner.lookup('service:version'); - - await this.renderComponent(); - - assert.dom(CLIENT_COUNT.counts.startLabel).hasText('Client counting start date', 'Label renders for OSS'); - assert - .dom(CLIENT_COUNT.counts.description) - .hasText( - 'This date is when client counting starts. Without this starting point, the data shown is not reliable.', - 'Description renders for OSS' - ); - - versionService.set('type', 'enterprise'); - await settled(); - - assert.dom(CLIENT_COUNT.counts.startLabel).hasText('Billing start month', 'Label renders for Enterprise'); - assert - .dom(CLIENT_COUNT.counts.description) - .hasText( - 'This date comes from your license, and defines when client counting starts. Without this starting point, the data shown is not reliable.', - 'Description renders for Enterprise' - ); - }); - test('it should populate start and end month displays', async function (assert) { await this.renderComponent(); - assert.dom(CLIENT_COUNT.counts.startMonth).hasText('July 2023', 'Start month renders'); - assert - .dom(CLIENT_COUNT.calendarWidget.trigger) - .hasText('Jul 2023 - Jan 2024', 'Start and end months render in filter bar'); + assert.dom(CLIENT_COUNT.dateRange.dateDisplay('start')).hasText('July 2023', 'Start month renders'); + assert.dom(CLIENT_COUNT.dateRange.dateDisplay('end')).hasText('January 2024', 'End month renders'); }); test('it should render no data empty state', async function (assert) { @@ -123,31 +95,41 @@ module('Integration | Component | clients | Page::Counts', function (hooks) { }); test('it should send correct values on start and end date change', async function (assert) { - assert.expect(4); + assert.expect(3); + const jan23start = getUnixTime(new Date('2023-01-01T00:00:00Z')); + const dec23end = getUnixTime(new Date('2023-12-31T00:00:00Z')); + const jan24end = getUnixTime(new Date('2024-01-31T00:00:00Z')); - let expected = { start_time: getUnixTime(new Date('2023-01-01T00:00:00Z')), end_time: END_TIME }; + const expected = { start_time: START_TIME, end_time: END_TIME }; this.onFilterChange = (params) => { assert.deepEqual(params, expected, 'Correct values sent on filter change'); - this.startTimestamp = params.start_time || START_TIME; - this.endTimestamp = params.end_time || END_TIME; + this.set('startTimestamp', params.start_time || START_TIME); + this.set('endTimestamp', params.end_time || END_TIME); }; - + // page starts with default billing dates, which are july 23 - jan 24 await this.renderComponent(); - await dateDropdownSelect('January', '2023'); - expected.start_time = END_TIME; - await click(CLIENT_COUNT.calendarWidget.trigger); - await click(CLIENT_COUNT.calendarWidget.currentMonth); + // First, change only the start date + expected.start_time = jan23start; + // the end date which is first set to STATIC_NOW gets recalculated + // to the end of given month/year on date range change + expected.end_time = jan24end; + await click(CLIENT_COUNT.dateRange.edit); + await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2023-01'); + await click(GENERAL.saveButton); - expected.start_time = getUnixTime(this.config.billingStartTimestamp); - await click(CLIENT_COUNT.calendarWidget.trigger); - await click(CLIENT_COUNT.calendarWidget.currentBillingPeriod); + // Then change only the end date + expected.end_time = dec23end; + await click(CLIENT_COUNT.dateRange.edit); + await fillIn(CLIENT_COUNT.dateRange.editDate('end'), '2023-12'); + await click(GENERAL.saveButton); - expected = { end_time: getUnixTime(new Date('2023-12-31T00:00:00Z')) }; - await click(CLIENT_COUNT.calendarWidget.trigger); - await click(CLIENT_COUNT.calendarWidget.customEndMonth); - await click(CLIENT_COUNT.calendarWidget.previousYear); - await click(CLIENT_COUNT.calendarWidget.calendarMonth('December')); + // Then reset to billing which should reset the params + expected.start_time = undefined; + expected.end_time = undefined; + await click(CLIENT_COUNT.dateRange.edit); + await click(CLIENT_COUNT.dateRange.reset); + await click(GENERAL.saveButton); }); test('it should render namespace and auth mount filters', async function (assert) { @@ -254,9 +236,7 @@ module('Integration | Component | clients | Page::Counts', function (hooks) { await this.renderComponent(); assert.dom(GENERAL.emptyStateTitle).hasText('No start date found', 'Empty state renders'); - assert - .dom(CLIENT_COUNT.counts.startDropdown) - .exists('Date dropdown renders when start time is not provided'); + assert.dom(CLIENT_COUNT.dateRange.set).exists(); }); test('it should render catch all empty state', async function (assert) { diff --git a/ui/tests/integration/components/date-dropdown-test.js b/ui/tests/integration/components/date-dropdown-test.js deleted file mode 100644 index 56a5654754..0000000000 --- a/ui/tests/integration/components/date-dropdown-test.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * 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 { click, render } from '@ember/test-helpers'; -import { hbs } from 'ember-cli-htmlbars'; -import { ARRAY_OF_MONTHS } from 'core/utils/date-formatters'; -import timestamp from 'core/utils/timestamp'; - -const SELECTORS = { - monthDropdown: '[data-test-toggle-month]', - specificMonth: (m) => `[data-test-dropdown-month="${m}"]`, - yearDropdown: '[data-test-toggle-year]', - specificYear: (y) => `[data-test-dropdown-year="${y}"]`, - submitButton: '[data-test-date-dropdown-submit]', - monthOptions: '[data-test-dropdown-month]', -}; - -module('Integration | Component | date-dropdown', function (hooks) { - setupRenderingTest(hooks); - - hooks.beforeEach(function () { - sinon.replace(timestamp, 'now', sinon.fake.returns(new Date('2018-04-03T14:15:30'))); - }); - - test('it renders dropdown', async function (assert) { - await render(hbs` -
- -
- `); - assert.dom(SELECTORS.submitButton).hasText('Submit', 'button renders default text'); - }); - - test('it renders dropdown and selects month and year', async function (assert) { - assert.expect(26); - const parentAction = (args) => { - assert.propEqual( - args, - { - dateType: 'start', - monthIdx: 1, - monthName: 'February', - year: 2016, - }, - 'sends correct args to parent' - ); - }; - this.set('parentAction', parentAction); - - await render(hbs` -
- -
- `); - assert.dom(SELECTORS.submitButton).isDisabled('button is disabled when no month or year selected'); - - await click(SELECTORS.monthDropdown); - - assert.dom(SELECTORS.monthOptions).exists({ count: 12 }, 'dropdown has 12 months'); - ARRAY_OF_MONTHS.forEach((month) => { - assert.dom(SELECTORS.specificMonth(month)).hasText(`${month}`, `dropdown includes ${month}`); - }); - - await click(SELECTORS.specificMonth('February')); - assert.dom(SELECTORS.monthDropdown).hasText('February', 'dropdown shows selected month'); - assert.dom('.ember-basic-dropdown-content').doesNotExist('dropdown closes after selecting month'); - - await click(SELECTORS.yearDropdown); - - assert.dom('[data-test-dropdown-year]').exists({ count: 5 }, 'dropdown has 5 years'); - for (const year of [2018, 2017, 2016, 2015, 2014]) { - assert.dom(SELECTORS.specificYear(year)).exists(); - } - - await click('[data-test-dropdown-year="2016"]'); - assert.dom(SELECTORS.yearDropdown).hasText(`2016`, `dropdown shows selected year`); - assert.dom('.ember-basic-dropdown-content').doesNotExist('dropdown closes after selecting year'); - assert.dom(SELECTORS.submitButton).isNotDisabled('button enabled when month and year selected'); - - await click(SELECTORS.submitButton); - }); - - test('selecting month first: current year enabled when current month selected', async function (assert) { - assert.expect(5); - await render(hbs` -
- -
- `); - // select current month - await click(SELECTORS.monthDropdown); - await click(SELECTORS.specificMonth('January')); - await click(SELECTORS.yearDropdown); - // all years should be selectable - for (const year of [2018, 2017, 2016, 2015, 2014]) { - assert.dom(SELECTORS.specificYear(year)).isNotDisabled(`year ${year} is selectable`); - } - }); - - test('selecting month first: it disables current year when future months selected', async function (assert) { - assert.expect(5); - await render(hbs` -
- -
- `); - - // select future month - await click(SELECTORS.monthDropdown); - await click(SELECTORS.specificMonth('June')); - await click(SELECTORS.yearDropdown); - - assert.dom(SELECTORS.specificYear(2018)).isDisabled(`current year is disabled`); - // previous years should be selectable - for (const year of [2017, 2016, 2015, 2014]) { - assert.dom(SELECTORS.specificYear(year)).isNotDisabled(`year ${year} is selectable`); - } - }); - - test('selecting year first: it disables future months when current year selected', async function (assert) { - assert.expect(12); - await render(hbs` -
- -
- `); - await click(SELECTORS.yearDropdown); - await click(SELECTORS.specificYear(2018)); - await click(SELECTORS.monthDropdown); - - const expectedSelectable = ['January', 'February', 'March', 'April']; - ARRAY_OF_MONTHS.forEach((month) => { - if (expectedSelectable.includes(month)) { - assert.dom(SELECTORS.specificMonth(month)).isNotDisabled(`${month} is selectable for current year`); - } else { - assert.dom(SELECTORS.specificMonth(month)).isDisabled(`${month} is disabled for current year`); - } - }); - }); - - test('selecting year first: it enables all months when past year is selected', async function (assert) { - assert.expect(12); - await render(hbs` -
- -
- `); - - await click(SELECTORS.yearDropdown); - await click(SELECTORS.specificYear(2017)); - await click(SELECTORS.monthDropdown); - - ARRAY_OF_MONTHS.forEach((month) => { - assert.dom(SELECTORS.specificMonth(month)).isNotDisabled(`${month} is selectable for previous year`); - }); - }); -});