From d55c6b777d335cc678bef3865906dbac24038d1b Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 17 Jul 2024 15:28:27 -0500 Subject: [PATCH 01/14] update adapter to make call without params --- ui/app/adapters/clients/activity.js | 41 ++++++++++++------- .../unit/adapters/clients-activity-test.js | 30 ++++++++++---- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/ui/app/adapters/clients/activity.js b/ui/app/adapters/clients/activity.js index 3e9455228e94..419d951fabe2 100644 --- a/ui/app/adapters/clients/activity.js +++ b/ui/app/adapters/clients/activity.js @@ -8,31 +8,42 @@ import { formatDateObject } from 'core/utils/client-count-utils'; import { debug } from '@ember/debug'; export default class ActivityAdapter extends ApplicationAdapter { + formatTimeParam(dateObj, isEnd = false) { + let formatted; + if (dateObj) { + try { + const iso = dateObj.timestamp || formatDateObject(dateObj, isEnd); + formatted = iso; + } catch (e) { + // carry on + } + } + return formatted; + } // javascript localizes new Date() objects but all activity log data is stored in UTC // create date object from user's input using Date.UTC() then send to backend as unix // time params from the backend are formatted as a zulu timestamp formatQueryParams(queryParams) { - if (queryParams?.current_billing_period) { - // { current_billing_period: true } automatically queries the activity log - // from the builtin license start timestamp to the current month - return queryParams; + const query = {}; + const start = this.formatTimeParam(queryParams?.start_time); + const end = this.formatTimeParam(queryParams?.end_time, true); + if (start) { + query.start_time = start; } - let { start_time, end_time } = queryParams; - start_time = start_time.timestamp || formatDateObject(start_time); - end_time = end_time.timestamp || formatDateObject(end_time, true); - return { start_time, end_time }; + if (end) { + query.end_time = end; + } + return query; } queryRecord(store, type, query) { const url = `${this.buildURL()}/internal/counters/activity`; const queryParams = this.formatQueryParams(query); - if (queryParams) { - return this.ajax(url, 'GET', { data: queryParams }).then((resp) => { - const response = resp || {}; - response.id = response.request_id || 'no-data'; - return response; - }); - } + return this.ajax(url, 'GET', { data: queryParams }).then((resp) => { + const response = resp || {}; + response.id = response.request_id || 'no-data'; + return response; + }); } urlForFindRecord(id) { diff --git a/ui/tests/unit/adapters/clients-activity-test.js b/ui/tests/unit/adapters/clients-activity-test.js index 7d23d1e9758b..0e732c793342 100644 --- a/ui/tests/unit/adapters/clients-activity-test.js +++ b/ui/tests/unit/adapters/clients-activity-test.js @@ -137,17 +137,33 @@ module('Unit | Adapter | clients activity', function (hooks) { this.store.queryRecord(this.modelName, queryParams); }); - test('it sends current billing period boolean if provided', async function (assert) { + test('it sends without query if no dates provided', async function (assert) { assert.expect(1); this.server.get('sys/internal/counters/activity', (schema, req) => { - assert.propEqual( - req.queryParams, - { current_billing_period: 'true' }, - 'it passes current_billing_period to query record' - ); + assert.propEqual(req.queryParams, {}); + }); + + this.store.queryRecord(this.modelName, { foo: 'bar' }); + }); + + test('it sends without query if no valid dates provided', async function (assert) { + assert.expect(1); + + this.server.get('sys/internal/counters/activity', (schema, req) => { + assert.propEqual(req.queryParams, {}); + }); + + this.store.queryRecord(this.modelName, { start_time: 'bar' }); + }); + + test('it handles empty query gracefully', async function (assert) { + assert.expect(1); + + this.server.get('sys/internal/counters/activity', (schema, req) => { + assert.propEqual(req.queryParams, {}); }); - this.store.queryRecord(this.modelName, { current_billing_period: true }); + this.store.queryRecord(this.modelName, {}); }); }); From 07ab143f8cddaa2ceb6fdc629737d89d8f42b2d8 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 17 Jul 2024 15:44:51 -0500 Subject: [PATCH 02/14] Update counts in route, and downstream effects --- ui/app/components/clients/activity.ts | 14 +--- ui/app/components/clients/page/counts.hbs | 4 +- ui/app/components/clients/page/counts.ts | 30 ++++----- ui/app/components/clients/page/overview.hbs | 4 +- ui/app/routes/vault/cluster/clients/counts.ts | 65 +++++++++++++------ ui/lib/core/addon/utils/client-count-utils.ts | 11 ---- .../utils/client-count-utils-test.js | 35 ---------- 7 files changed, 63 insertions(+), 100 deletions(-) diff --git a/ui/app/components/clients/activity.ts b/ui/app/components/clients/activity.ts index f22134b94a43..10a419b2e992 100644 --- a/ui/app/components/clients/activity.ts +++ b/ui/app/components/clients/activity.ts @@ -7,7 +7,7 @@ // contains getters that filter and extract data from activity model for use in charts import Component from '@glimmer/component'; -import { isSameMonth, fromUnixTime } from 'date-fns'; +import { isSameMonth } from 'date-fns'; import { parseAPITimestamp } from 'core/utils/date-formatters'; import { calculateAverage } from 'vault/utils/chart-helpers'; import { filterVersionHistory, hasMountsKey, hasNamespacesKey } from 'core/utils/client-count-utils'; @@ -24,8 +24,8 @@ import type { interface Args { activity: ClientsActivityModel; versionHistory: ClientsVersionHistoryModel[]; - startTimestamp: number; - endTimestamp: number; + startTimestamp: string; + endTimestamp: string; namespace: string; mountPath: string; } @@ -40,14 +40,6 @@ export default class ClientsActivityComponent extends Component { return calculateAverage(data, key); }; - get startTimeISO() { - return fromUnixTime(this.args.startTimestamp).toISOString(); - } - - get endTimeISO() { - return fromUnixTime(this.args.endTimestamp).toISOString(); - } - get byMonthActivityData() { const { activity, namespace } = this.args; return namespace ? this.filteredActivityByMonth : activity.byMonth; diff --git a/ui/app/components/clients/page/counts.hbs b/ui/app/components/clients/page/counts.hbs index bb369ced1f11..38b8fd840cc1 100644 --- a/ui/app/components/clients/page/counts.hbs +++ b/ui/app/components/clients/page/counts.hbs @@ -19,8 +19,8 @@

diff --git a/ui/app/components/clients/page/counts.ts b/ui/app/components/clients/page/counts.ts index b2cf003dddc4..bd53145a54b3 100644 --- a/ui/app/components/clients/page/counts.ts +++ b/ui/app/components/clients/page/counts.ts @@ -6,7 +6,7 @@ import Component from '@glimmer/component'; import { service } from '@ember/service'; import { action } from '@ember/object'; -import { fromUnixTime, isSameMonth, isAfter } from 'date-fns'; +import { isSameMonth, isAfter } from 'date-fns'; import { parseAPITimestamp } from 'core/utils/date-formatters'; import { filterVersionHistory } from 'core/utils/client-count-utils'; @@ -21,11 +21,11 @@ interface Args { activity: ClientsActivityModel; activityError?: AdapterError; config: ClientsConfigModel; - endTimestamp: number; + endTimestamp: string; // ISO format mountPath: string; namespace: string; onFilterChange: CallableFunction; - startTimestamp: number; + startTimestamp: string; // ISO format versionHistory: ClientsVersionHistoryModel[]; } @@ -34,29 +34,21 @@ export default class ClientsCountsPageComponent extends Component { @service declare readonly version: VersionService; @service declare readonly store: StoreService; - get startTimestampISO() { - return this.args.startTimestamp ? fromUnixTime(this.args.startTimestamp).toISOString() : null; - } - - get endTimestampISO() { - return this.args.endTimestamp ? fromUnixTime(this.args.endTimestamp).toISOString() : null; - } - get formattedStartDate() { - return this.startTimestampISO ? parseAPITimestamp(this.startTimestampISO, 'MMMM yyyy') : null; + return this.args.startTimestamp ? parseAPITimestamp(this.args.startTimestamp, 'MMMM yyyy') : null; } // returns text for empty state message if noActivityData get dateRangeMessage() { - if (this.startTimestampISO && this.endTimestampISO) { + if (this.args.startTimestamp && this.args.endTimestamp) { const endMonth = isSameMonth( - parseAPITimestamp(this.startTimestampISO) as Date, - parseAPITimestamp(this.endTimestampISO) as Date + parseAPITimestamp(this.args.startTimestamp) as Date, + parseAPITimestamp(this.args.endTimestamp) as Date ) ? '' - : `to ${parseAPITimestamp(this.endTimestampISO, 'MMMM yyyy')}`; + : `to ${parseAPITimestamp(this.args.endTimestamp, 'MMMM yyyy')}`; // completes the message 'No data received from { dateRangeMessage }' - return `from ${parseAPITimestamp(this.startTimestampISO, 'MMMM yyyy')} ${endMonth}`; + return `from ${parseAPITimestamp(this.args.startTimestamp, 'MMMM yyyy')} ${endMonth}`; } return null; } @@ -127,9 +119,9 @@ export default class ClientsCountsPageComponent extends Component { // show banner if startTime returned from activity log (response) is after the queried startTime const { activity, config } = this.args; const activityStartDateObject = parseAPITimestamp(activity.startTime) as Date; - const queryStartDateObject = parseAPITimestamp(this.startTimestampISO) as Date; + const queryStartDateObject = parseAPITimestamp(this.args.startTimestamp) as Date; const isEnterprise = - this.startTimestampISO === config.billingStartTimestamp?.toISOString() && this.version.isEnterprise; + this.args.startTimestamp === config.billingStartTimestamp?.toISOString() && this.version.isEnterprise; const message = isEnterprise ? 'Your license start date is' : 'You requested data from'; if ( diff --git a/ui/app/components/clients/page/overview.hbs b/ui/app/components/clients/page/overview.hbs index f069423df279..e8abfc6abfed 100644 --- a/ui/app/components/clients/page/overview.hbs +++ b/ui/app/components/clients/page/overview.hbs @@ -21,8 +21,8 @@ @totalClientAttribution={{this.totalClientAttribution}} @newClientAttribution={{this.newClientAttribution}} @selectedNamespace={{@namespace}} - @startTimestamp={{this.startTimeISO}} - @endTimestamp={{this.endTimeISO}} + @startTimestamp={{@startTimestamp}} + @endTimestamp={{@endTimestamp}} @responseTimestamp={{@activity.responseTimestamp}} @isHistoricalMonth={{and (not this.isCurrentMonth) (not this.isDateRange)}} @upgradesDuringActivity={{this.upgradesDuringActivity}} diff --git a/ui/app/routes/vault/cluster/clients/counts.ts b/ui/app/routes/vault/cluster/clients/counts.ts index b2322970db80..814a51da8ba8 100644 --- a/ui/app/routes/vault/cluster/clients/counts.ts +++ b/ui/app/routes/vault/cluster/clients/counts.ts @@ -5,8 +5,7 @@ import Route from '@ember/routing/route'; import { service } from '@ember/service'; -import timestamp from 'core/utils/timestamp'; -import { getUnixTime } from 'date-fns'; +import { fromUnixTime } from 'date-fns'; import type FlagsService from 'vault/services/flags'; import type StoreService from 'vault/services/store'; @@ -14,7 +13,6 @@ import type VersionService from 'vault/services/version'; import type { ModelFrom } from 'vault/vault/route'; import type ClientsRoute from '../clients'; import type ClientsCountsController from 'vault/controllers/vault/cluster/clients/counts'; -import { setStartTimeQuery } from 'core/utils/client-count-utils'; export interface ClientsCountsRouteParams { start_time?: string | number | undefined; @@ -39,38 +37,65 @@ export default class ClientsCountsRoute extends Route { return this.flags.fetchActivatedFlags(); } - async getActivity(start_time: number | null, end_time: number) { + paramOrResponseTimestamp( + qpMillisString: string | number | undefined, + activityTimeStamp: string | undefined + ) { + let timestamp: string | undefined; + const millis = Number(qpMillisString); + if (!isNaN(millis)) { + timestamp = fromUnixTime(millis).toISOString(); + } + // fallback to activity timestamp only if there was no query param + if (!timestamp && activityTimeStamp) { + timestamp = activityTimeStamp; + } + return timestamp; + } + + async getActivity(params: ClientsCountsRouteParams) { let activity, activityError; - // if there is no start_time we want the user to manually choose a date - // in that case bypass the query so that the user isn't stuck viewing the activity error - if (start_time) { + const query = { + // start and end params are optional -- if not provided, will fallback to API default + start_time: this.formatTimeQuery(params?.start_time), + end_time: this.formatTimeQuery(params?.end_time), + }; + // if CE without start time we want to skip the activity call + // so that the user is forced to choose a date range + if (this.version.isEnterprise || params.start_time) { try { - activity = await this.store.queryRecord('clients/activity', { - start_time: { timestamp: start_time }, - end_time: { timestamp: end_time }, - }); + activity = await this.store.queryRecord('clients/activity', query); } catch (error) { activityError = error; } } - return { activity, activityError }; + return { + activity, + activityError, + }; + } + + // Takes the string URL param and formats it as the adapter expects it, + // if it exists and is valid + formatTimeQuery(param: string | number | undefined) { + let timeParam: { timestamp: number } | undefined; + const millis = Number(param); + if (!isNaN(millis)) { + timeParam = { timestamp: millis }; + } + return timeParam; } async model(params: ClientsCountsRouteParams) { const { config, versionHistory } = this.modelFor('vault.cluster.clients') as ModelFrom; - // only enterprise versions will have a relevant billing start date, if null users must select initial start time - const startTime = setStartTimeQuery(this.version.isEnterprise, config); - - const startTimestamp = Number(params.start_time) || startTime; - const endTimestamp = Number(params.end_time) || getUnixTime(timestamp.now()); - const { activity, activityError } = await this.getActivity(startTimestamp, endTimestamp); + const { activity, activityError } = await this.getActivity(params); return { activity, activityError, config, - endTimestamp, - startTimestamp, + startTimestamp: this.paramOrResponseTimestamp(params?.start_time, activity.startDate), + endTimestamp: this.paramOrResponseTimestamp(params?.end_time, activity.endDate), versionHistory, }; } diff --git a/ui/lib/core/addon/utils/client-count-utils.ts b/ui/lib/core/addon/utils/client-count-utils.ts index 69091f888ea1..243cd35741c5 100644 --- a/ui/lib/core/addon/utils/client-count-utils.ts +++ b/ui/lib/core/addon/utils/client-count-utils.ts @@ -66,17 +66,6 @@ export const filterVersionHistory = ( return []; }; -export const setStartTimeQuery = ( - isEnterprise: boolean, - config: ClientsConfigModel | Record -) => { - // CE versions have no license and so the start time defaults to "0001-01-01T00:00:00Z" - if (isEnterprise && _hasConfig(config)) { - return getUnixTime(config.billingStartTimestamp); - } - return null; -}; - // METHODS FOR SERIALIZING ACTIVITY RESPONSE export const formatDateObject = (dateObj: { monthIdx: number; year: number }, isEnd: boolean) => { const { year, monthIdx } = dateObj; diff --git a/ui/tests/integration/utils/client-count-utils-test.js b/ui/tests/integration/utils/client-count-utils-test.js index d6da1a5a04ef..703d098dc54f 100644 --- a/ui/tests/integration/utils/client-count-utils-test.js +++ b/ui/tests/integration/utils/client-count-utils-test.js @@ -13,7 +13,6 @@ import { destructureClientCounts, namespaceArrayToObject, sortMonthsByTimestamp, - setStartTimeQuery, } from 'core/utils/client-count-utils'; import clientsHandler from 'vault/mirage/handlers/clients'; import { @@ -28,9 +27,6 @@ used to normalize the sys/counters/activity response in the clients/activity serializer. these functions are tested individually here, instead of all at once in a serializer test for easier debugging */ - -// TODO refactor tests into a module for each util method, then make each assertion its separate tests - module('Integration | Util | client count utils', function (hooks) { setupTest(hooks); @@ -443,35 +439,4 @@ module('Integration | Util | client count utils', function (hooks) { 'it formats combined data for monthly namespaces_by_key spanning upgrade to 1.10' ); }); - - test('setStartTimeQuery: it returns start time query for activity log', async function (assert) { - assert.expect(6); - const apiPath = 'sys/internal/counters/config'; - assert.strictEqual(setStartTimeQuery(true, {}), null, `it returns null if no permission to ${apiPath}`); - assert.strictEqual( - setStartTimeQuery(false, {}), - null, - `it returns null for community edition and no permission to ${apiPath}` - ); - assert.strictEqual( - setStartTimeQuery(true, { billingStartTimestamp: new Date('2022-06-08T00:00:00Z') }), - 1654646400, - 'it returns unix time if enterprise and billing_start_timestamp exists' - ); - assert.strictEqual( - setStartTimeQuery(false, { billingStartTimestamp: new Date('0001-01-01T00:00:00Z') }), - null, - 'it returns null time for community edition even if billing_start_timestamp exists' - ); - assert.strictEqual( - setStartTimeQuery(false, { foo: 'bar' }), - null, - 'it returns null if billing_start_timestamp key does not exist' - ); - assert.strictEqual( - setStartTimeQuery(false, undefined), - null, - 'fails gracefully if no config model is passed' - ); - }); }); From aa27711c8090cb7c42698b5ff8390ed5f1c086d7 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Fri, 19 Jul 2024 09:53:42 -0500 Subject: [PATCH 03/14] safeguard against empty activity response, update tests --- ui/app/routes/vault/cluster/clients/counts.ts | 5 ++--- ui/tests/acceptance/clients/counts-test.js | 8 ++++---- .../components/clients/page/counts-test.js | 20 +++++++++++++------ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/ui/app/routes/vault/cluster/clients/counts.ts b/ui/app/routes/vault/cluster/clients/counts.ts index 814a51da8ba8..85777dd8d428 100644 --- a/ui/app/routes/vault/cluster/clients/counts.ts +++ b/ui/app/routes/vault/cluster/clients/counts.ts @@ -89,13 +89,12 @@ export default class ClientsCountsRoute extends Route { async model(params: ClientsCountsRouteParams) { const { config, versionHistory } = this.modelFor('vault.cluster.clients') as ModelFrom; const { activity, activityError } = await this.getActivity(params); - return { activity, activityError, config, - startTimestamp: this.paramOrResponseTimestamp(params?.start_time, activity.startDate), - endTimestamp: this.paramOrResponseTimestamp(params?.end_time, activity.endDate), + startTimestamp: this.paramOrResponseTimestamp(params?.start_time, activity?.startTime), + endTimestamp: this.paramOrResponseTimestamp(params?.end_time, activity?.endTime), versionHistory, }; } diff --git a/ui/tests/acceptance/clients/counts-test.js b/ui/tests/acceptance/clients/counts-test.js index 4c283f4b83e3..ba99641bc9ed 100644 --- a/ui/tests/acceptance/clients/counts-test.js +++ b/ui/tests/acceptance/clients/counts-test.js @@ -43,19 +43,19 @@ 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.dateRange.edit); - await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2020-03'); - await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2022-02'); + await fillIn(CLIENT_COUNT.dateRange.editDate('start'), '2023-03'); + await fillIn(CLIENT_COUNT.dateRange.editDate('end'), '2023-10'); await click(GENERAL.saveButton); assert.strictEqual( currentURL(), - '/vault/clients/counts/overview?end_time=1706659200&start_time=1643673600', + '/vault/clients/counts/overview?end_time=1698710400&start_time=1677628800', 'Start and end times added as query params' ); await click(GENERAL.tab('token')); assert.strictEqual( currentURL(), - '/vault/clients/counts/token?end_time=1706659200&start_time=1643673600', + '/vault/clients/counts/token?end_time=1698710400&start_time=1677628800', 'Start and end times persist through child route change' ); diff --git a/ui/tests/integration/components/clients/page/counts-test.js b/ui/tests/integration/components/clients/page/counts-test.js index 4577d695c6df..07644a6590e9 100644 --- a/ui/tests/integration/components/clients/page/counts-test.js +++ b/ui/tests/integration/components/clients/page/counts-test.js @@ -9,7 +9,7 @@ import { setupMirage } from 'ember-cli-mirage/test-support'; 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 { fromUnixTime, getUnixTime } from 'date-fns'; import { GENERAL } from 'vault/tests/helpers/general-selectors'; import { CLIENT_COUNT } from 'vault/tests/helpers/clients/client-count-selectors'; import { selectChoose } from 'ember-power-select/test-support'; @@ -19,6 +19,8 @@ import { allowAllCapabilitiesStub } from 'vault/tests/helpers/stubs'; const START_TIME = getUnixTime(LICENSE_START); const END_TIME = getUnixTime(STATIC_NOW); +const START_ISO = LICENSE_START.toISOString(); +const END_ISO = STATIC_NOW.toISOString(); module('Integration | Component | clients | Page::Counts', function (hooks) { setupRenderingTest(hooks); @@ -35,8 +37,8 @@ module('Integration | Component | clients | Page::Counts', function (hooks) { }; this.activity = await this.store.queryRecord('clients/activity', activityQuery); this.config = await this.store.queryRecord('clients/config', {}); - this.startTimestamp = START_TIME; - this.endTimestamp = END_TIME; + this.startTimestamp = START_ISO; + this.endTimestamp = END_ISO; this.versionHistory = []; this.renderComponent = () => render(hbs` @@ -103,8 +105,14 @@ module('Integration | Component | clients | Page::Counts', function (hooks) { const expected = { start_time: START_TIME, end_time: END_TIME }; this.onFilterChange = (params) => { assert.deepEqual(params, expected, 'Correct values sent on filter change'); - this.set('startTimestamp', params.start_time || START_TIME); - this.set('endTimestamp', params.end_time || END_TIME); + // in the app, the timestamp choices trigger a qp refresh as millis from epoch, + // but in the model they are translated from millis to ISO timestamps before being + // passed to this component. Mock that behavior here. + this.set( + 'startTimestamp', + params?.start_time ? fromUnixTime(params.start_time).toISOString() : START_ISO + ); + this.set('endTimestamp', params?.end_time ? fromUnixTime(params.end_time).toISOString() : END_ISO); }; // page starts with default billing dates, which are july 23 - jan 24 await this.renderComponent(); @@ -168,7 +176,7 @@ module('Integration | Component | clients | Page::Counts', function (hooks) { }); test('it should render start time discrepancy alert', async function (assert) { - this.startTimestamp = getUnixTime(new Date('2022-06-01T00:00:00Z')); + this.startTimestamp = new Date('2022-06-01T00:00:00Z').toISOString(); await this.renderComponent(); From aea20ede2eeee126c6a2a6babce078701455faa2 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Fri, 19 Jul 2024 09:55:43 -0500 Subject: [PATCH 04/14] edit test so it plays nicely between ent & CE --- ui/app/components/clients/date-range.hbs | 2 +- ui/tests/helpers/clients/client-count-selectors.ts | 1 - ui/tests/integration/components/clients/date-range-test.js | 4 ++-- ui/tests/integration/components/clients/page/counts-test.js | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ui/app/components/clients/date-range.hbs b/ui/app/components/clients/date-range.hbs index 0d5ed63634bc..1298345eb92b 100644 --- a/ui/app/components/clients/date-range.hbs +++ b/ui/app/components/clients/date-range.hbs @@ -27,7 +27,7 @@ @text="Set date range" @icon="edit" {{on "click" (fn (mut this.showEditModal) true)}} - data-test-set-date-range + data-test-date-range-edit /> {{/if}} diff --git a/ui/tests/helpers/clients/client-count-selectors.ts b/ui/tests/helpers/clients/client-count-selectors.ts index 53691cfabfa3..ef820a6860d2 100644 --- a/ui/tests/helpers/clients/client-count-selectors.ts +++ b/ui/tests/helpers/clients/client-count-selectors.ts @@ -14,7 +14,6 @@ export const CLIENT_COUNT = { }, 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}"]`, diff --git a/ui/tests/integration/components/clients/date-range-test.js b/ui/tests/integration/components/clients/date-range-test.js index beec2fe491f8..2a5c6bb60976 100644 --- a/ui/tests/integration/components/clients/date-range-test.js +++ b/ui/tests/integration/components/clients/date-range-test.js @@ -33,9 +33,9 @@ module('Integration | Component | clients/date-range', function (hooks) { this.startTime = undefined; await this.renderComponent(); - assert.dom(DATE_RANGE.set).exists(); + assert.dom(DATE_RANGE.edit).hasText('Set date range'); - await click(DATE_RANGE.set); + await click(DATE_RANGE.edit); assert.dom(DATE_RANGE.editModal).exists(); assert.dom(DATE_RANGE.editDate('start')).hasValue(''); await fillIn(DATE_RANGE.editDate('start'), '2018-01'); diff --git a/ui/tests/integration/components/clients/page/counts-test.js b/ui/tests/integration/components/clients/page/counts-test.js index 07644a6590e9..df14cf43f3c9 100644 --- a/ui/tests/integration/components/clients/page/counts-test.js +++ b/ui/tests/integration/components/clients/page/counts-test.js @@ -244,7 +244,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.dateRange.set).exists(); + assert.dom(CLIENT_COUNT.dateRange.edit).hasText('Set date range'); }); test('it should render catch all empty state', async function (assert) { From c8b5fce3d375bf671c9fba08567524fc8188ecf4 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Fri, 19 Jul 2024 11:21:44 -0500 Subject: [PATCH 05/14] use first month timestamp instead of startTime which reflects first month with data --- ui/app/routes/vault/cluster/clients/counts.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/app/routes/vault/cluster/clients/counts.ts b/ui/app/routes/vault/cluster/clients/counts.ts index 85777dd8d428..ab7e35a6b2f1 100644 --- a/ui/app/routes/vault/cluster/clients/counts.ts +++ b/ui/app/routes/vault/cluster/clients/counts.ts @@ -93,7 +93,7 @@ export default class ClientsCountsRoute extends Route { activity, activityError, config, - startTimestamp: this.paramOrResponseTimestamp(params?.start_time, activity?.startTime), + startTimestamp: this.paramOrResponseTimestamp(params?.start_time, activity?.byMonth[0].timestamp), endTimestamp: this.paramOrResponseTimestamp(params?.end_time, activity?.endTime), versionHistory, }; From cbb037ea2258d98d59a9d044df867d6e410a9e50 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Fri, 19 Jul 2024 11:56:03 -0500 Subject: [PATCH 06/14] Update part of activity response used for start date, add test --- ui/app/routes/vault/cluster/clients/counts.ts | 2 +- ui/tests/acceptance/clients/counts-test.js | 79 +++++++++++++++++++ .../clients/counts/overview-test.js | 2 +- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/ui/app/routes/vault/cluster/clients/counts.ts b/ui/app/routes/vault/cluster/clients/counts.ts index ab7e35a6b2f1..81689bc931f3 100644 --- a/ui/app/routes/vault/cluster/clients/counts.ts +++ b/ui/app/routes/vault/cluster/clients/counts.ts @@ -93,7 +93,7 @@ export default class ClientsCountsRoute extends Route { activity, activityError, config, - startTimestamp: this.paramOrResponseTimestamp(params?.start_time, activity?.byMonth[0].timestamp), + startTimestamp: this.paramOrResponseTimestamp(params?.start_time, activity?.byMonth[0]?.timestamp), endTimestamp: this.paramOrResponseTimestamp(params?.end_time, activity?.endTime), versionHistory, }; diff --git a/ui/tests/acceptance/clients/counts-test.js b/ui/tests/acceptance/clients/counts-test.js index ba99641bc9ed..ab32f0718bb4 100644 --- a/ui/tests/acceptance/clients/counts-test.js +++ b/ui/tests/acceptance/clients/counts-test.js @@ -81,4 +81,83 @@ module('Acceptance | clients | counts', function (hooks) { 'You must be granted permissions to view this page. Ask your administrator if you think you should have access to the /v1/sys/internal/counters/activity endpoint.' ); }); + + test('it should use the first month timestamp from default response rather than response start_time', async function (assert) { + assert.expect(2); + const getCounts = () => { + return { + acme_clients: 0, + clients: 0, + entity_clients: 0, + non_entity_clients: 0, + secret_syncs: 0, + distinct_entities: 0, + non_entity_tokens: 1, + }; + }; + // set to enterprise because when community the initial activity call is skipped + this.owner.lookup('service:version').type = 'enterprise'; + this.server.get('/sys/internal/counters/activity', function () { + return { + request_id: 'some-activity-id', + data: { + start_time: '2023-04-01T00:00:00Z', // reflects the first month with data + end_time: '2023-04-30T00:00:00Z', + by_namespace: [], + months: [ + { + timestamp: '2023-02-01T00:00:00Z', + counts: null, + namespaces: null, + new_clients: null, + }, + { + timestamp: '2023-03-01T00:00:00Z', + counts: null, + namespaces: null, + new_clients: null, + }, + { + timestamp: '2023-04-01T00:00:00Z', + counts: getCounts(), + namespaces: [ + { + namespace_id: 'root', + namespace_path: '', + counts: getCounts(), + mounts: [ + { + mount_path: 'auth/authid/0', + counts: getCounts(), + }, + ], + }, + ], + new_clients: { + counts: getCounts(), + namespaces: [ + { + namespace_id: 'root', + namespace_path: '', + counts: getCounts(), + mounts: [ + { + mount_path: 'auth/authid/0', + counts: getCounts(), + }, + ], + }, + ], + }, + }, + ], + total: getCounts(), + }, + }; + }); + await visit('/vault/clients/counts/overview'); + assert.dom(CLIENT_COUNT.dateRange.dateDisplay('start')).hasText('February 2023'); + assert.dom(CLIENT_COUNT.dateRange.dateDisplay('end')).hasText('April 2023'); + assert.dom(CLIENT_COUNT.counts.startDiscrepancy).exists(); + }); }); diff --git a/ui/tests/acceptance/clients/counts/overview-test.js b/ui/tests/acceptance/clients/counts/overview-test.js index 6e5ba2dedc33..56d8ce5000ad 100644 --- a/ui/tests/acceptance/clients/counts/overview-test.js +++ b/ui/tests/acceptance/clients/counts/overview-test.js @@ -72,7 +72,7 @@ module('Acceptance | clients | overview', function (hooks) { .exists('new client attribution has empty state'); assert .dom(GENERAL.emptyStateSubtitle) - .hasText('There are no new clients for this namespace during this time period. '); + .hasText('There are no new clients for this namespace during this time period.'); assert.dom(CHARTS.container('total-clients')).exists('total client attribution chart shows'); // reset to billing period From 32dc1613c602db15cee0a844861bc57bedce155d Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Fri, 19 Jul 2024 12:01:23 -0500 Subject: [PATCH 07/14] Add changelog --- changelog/27816.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/27816.txt diff --git a/changelog/27816.txt b/changelog/27816.txt new file mode 100644 index 000000000000..92dd2d7bb90c --- /dev/null +++ b/changelog/27816.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: remove initial start/end parameters on the activity call for client counts dashboard. +``` \ No newline at end of file From 7d67067a05748b5f982f85d095ff7eaf53fe1725 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Fri, 19 Jul 2024 12:15:59 -0500 Subject: [PATCH 08/14] remove expect --- ui/tests/acceptance/clients/counts-test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/tests/acceptance/clients/counts-test.js b/ui/tests/acceptance/clients/counts-test.js index ab32f0718bb4..6db0217e1074 100644 --- a/ui/tests/acceptance/clients/counts-test.js +++ b/ui/tests/acceptance/clients/counts-test.js @@ -83,7 +83,6 @@ module('Acceptance | clients | counts', function (hooks) { }); test('it should use the first month timestamp from default response rather than response start_time', async function (assert) { - assert.expect(2); const getCounts = () => { return { acme_clients: 0, From d93988663eb98c1bb6c7c263cb445c388ec72ee9 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Fri, 19 Jul 2024 13:04:31 -0500 Subject: [PATCH 09/14] Remove language that implies CE can use default params on activity endpoint --- ui/app/components/clients/date-range.hbs | 2 +- ui/app/components/clients/date-range.ts | 4 +- .../components/clients/date-range-test.js | 41 ++++++++++++++++++- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/ui/app/components/clients/date-range.hbs b/ui/app/components/clients/date-range.hbs index 1298345eb92b..c652c9f9121b 100644 --- a/ui/app/components/clients/date-range.hbs +++ b/ui/app/components/clients/date-range.hbs @@ -87,7 +87,7 @@ data-test-date-range-validation >{{this.validationError}} {{/if}} - {{#if this.useDefaultDates}} + {{#if (and this.version.isEnterprise this.useDefaultDates)}} Dashboard will use the default date range from the API. diff --git a/ui/app/components/clients/date-range.ts b/ui/app/components/clients/date-range.ts index 4704a7c74589..9e87e413cf8b 100644 --- a/ui/app/components/clients/date-range.ts +++ b/ui/app/components/clients/date-range.ts @@ -67,8 +67,8 @@ export default class ClientsDateRangeComponent extends Component { } get validationError() { - if (this.useDefaultDates) { - // this means we want to reset, which is fine + if (this.useDefaultDates && this.version.isEnterprise) { + // this means we want to reset, which is fine for ent only return null; } if (!this.startDate || !this.endDate) { diff --git a/ui/tests/integration/components/clients/date-range-test.js b/ui/tests/integration/components/clients/date-range-test.js index 2a5c6bb60976..257c5b7ca86e 100644 --- a/ui/tests/integration/components/clients/date-range-test.js +++ b/ui/tests/integration/components/clients/date-range-test.js @@ -33,6 +33,8 @@ module('Integration | Component | clients/date-range', function (hooks) { this.startTime = undefined; await this.renderComponent(); + assert.dom(DATE_RANGE.dateDisplay('start')).doesNotExist(); + assert.dom(DATE_RANGE.dateDisplay('end')).doesNotExist(); assert.dom(DATE_RANGE.edit).hasText('Set date range'); await click(DATE_RANGE.edit); @@ -50,11 +52,13 @@ module('Integration | Component | clients/date-range', function (hooks) { assert.dom(DATE_RANGE.editModal).doesNotExist('closes modal'); }); - test('it renders the date range passed and can reset it', async function (assert) { + test('it renders the date range passed and can reset it (ent)', async function (assert) { + this.owner('service:version').version = 'enterprise'; await this.renderComponent(); assert.dom(DATE_RANGE.dateDisplay('start')).hasText('January 2018'); assert.dom(DATE_RANGE.dateDisplay('end')).hasText('January 2019'); + assert.dom(DATE_RANGE.edit).hasText('Edit'); await click(DATE_RANGE.edit); assert.dom(DATE_RANGE.editModal).exists(); @@ -70,7 +74,30 @@ module('Integration | Component | clients/date-range', function (hooks) { assert.deepEqual(this.onChange.args[0], [{ start_time: undefined, end_time: undefined }]); }); + test('it renders the date range passed and cannot reset it when community', async function (assert) { + this.owner('service:version').version = 'community'; + await this.renderComponent(); + + assert.dom(DATE_RANGE.dateDisplay('start')).hasText('January 2018'); + assert.dom(DATE_RANGE.dateDisplay('end')).hasText('January 2019'); + assert.dom(DATE_RANGE.edit).hasText('Edit'); + + 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.validation).hasText('You must supply both start and end dates.'); + await click(GENERAL.saveButton); + assert.false(this.onChange.called); + }); + test('it does not trigger onChange if date range invalid', async function (assert) { + this.owner('service:version').version = 'enterprise'; await this.renderComponent(); await click(DATE_RANGE.edit); @@ -90,6 +117,18 @@ module('Integration | Component | clients/date-range', function (hooks) { assert.dom(DATE_RANGE.editModal).doesNotExist(); }); + test('it does not trigger onChange when reset and CE', async function (assert) { + this.owner('service:version').version = 'community'; + await this.renderComponent(); + + await click(DATE_RANGE.edit); + + await click(DATE_RANGE.reset); + assert.dom(DATE_RANGE.validation).hasText('You must supply both start and end dates.'); + await click(GENERAL.saveButton); + assert.false(this.onChange.called); + }); + test('it resets the tracked values on close', async function (assert) { await this.renderComponent(); From ac5277ae79b31f1f16bbaa06a75546b1d8ec3efe Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Fri, 19 Jul 2024 13:19:08 -0500 Subject: [PATCH 10/14] whoops used this.owner wrong --- .../integration/components/clients/date-range-test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/tests/integration/components/clients/date-range-test.js b/ui/tests/integration/components/clients/date-range-test.js index 257c5b7ca86e..668d1147c54c 100644 --- a/ui/tests/integration/components/clients/date-range-test.js +++ b/ui/tests/integration/components/clients/date-range-test.js @@ -53,7 +53,7 @@ module('Integration | Component | clients/date-range', function (hooks) { }); test('it renders the date range passed and can reset it (ent)', async function (assert) { - this.owner('service:version').version = 'enterprise'; + this.owner.lookup('service:version').version = 'enterprise'; await this.renderComponent(); assert.dom(DATE_RANGE.dateDisplay('start')).hasText('January 2018'); @@ -75,7 +75,7 @@ module('Integration | Component | clients/date-range', function (hooks) { }); test('it renders the date range passed and cannot reset it when community', async function (assert) { - this.owner('service:version').version = 'community'; + this.owner.lookup('service:version').version = 'community'; await this.renderComponent(); assert.dom(DATE_RANGE.dateDisplay('start')).hasText('January 2018'); @@ -97,7 +97,7 @@ module('Integration | Component | clients/date-range', function (hooks) { }); test('it does not trigger onChange if date range invalid', async function (assert) { - this.owner('service:version').version = 'enterprise'; + this.owner.lookup('service:version').version = 'enterprise'; await this.renderComponent(); await click(DATE_RANGE.edit); @@ -118,7 +118,7 @@ module('Integration | Component | clients/date-range', function (hooks) { }); test('it does not trigger onChange when reset and CE', async function (assert) { - this.owner('service:version').version = 'community'; + this.owner.lookup('service:version').version = 'community'; await this.renderComponent(); await click(DATE_RANGE.edit); From 9784e4d6d2dd1a534bb3a3e61c403e2e7a91a602 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Tue, 30 Jul 2024 11:44:16 -0500 Subject: [PATCH 11/14] Clarify timestamp logic --- ui/app/routes/vault/cluster/clients/counts.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ui/app/routes/vault/cluster/clients/counts.ts b/ui/app/routes/vault/cluster/clients/counts.ts index 81689bc931f3..4170d02c4fb8 100644 --- a/ui/app/routes/vault/cluster/clients/counts.ts +++ b/ui/app/routes/vault/cluster/clients/counts.ts @@ -37,6 +37,9 @@ export default class ClientsCountsRoute extends Route { return this.flags.fetchActivatedFlags(); } + /** + * This method returns the query param timestamp if it exists. If not, it returns the activity timestamp value instead. + */ paramOrResponseTimestamp( qpMillisString: string | number | undefined, activityTimeStamp: string | undefined @@ -55,14 +58,14 @@ export default class ClientsCountsRoute extends Route { async getActivity(params: ClientsCountsRouteParams) { let activity, activityError; - const query = { - // start and end params are optional -- if not provided, will fallback to API default - start_time: this.formatTimeQuery(params?.start_time), - end_time: this.formatTimeQuery(params?.end_time), - }; // if CE without start time we want to skip the activity call // so that the user is forced to choose a date range if (this.version.isEnterprise || params.start_time) { + const query = { + // start and end params are optional -- if not provided, will fallback to API default + start_time: this.formatTimeQuery(params?.start_time), + end_time: this.formatTimeQuery(params?.end_time), + }; try { activity = await this.store.queryRecord('clients/activity', query); } catch (error) { @@ -93,6 +96,7 @@ export default class ClientsCountsRoute extends Route { activity, activityError, config, + // activity.startTime corresponds to first month with data, but we want first month returned or requested startTimestamp: this.paramOrResponseTimestamp(params?.start_time, activity?.byMonth[0]?.timestamp), endTimestamp: this.paramOrResponseTimestamp(params?.end_time, activity?.endTime), versionHistory, From 17b680a324c01bb29171c5a394499c7e55429428 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 31 Jul 2024 12:02:47 -0500 Subject: [PATCH 12/14] set version.type instead of version.version --- .../integration/components/clients/date-range-test.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/tests/integration/components/clients/date-range-test.js b/ui/tests/integration/components/clients/date-range-test.js index 668d1147c54c..2e3765d6a279 100644 --- a/ui/tests/integration/components/clients/date-range-test.js +++ b/ui/tests/integration/components/clients/date-range-test.js @@ -53,7 +53,7 @@ module('Integration | Component | clients/date-range', function (hooks) { }); test('it renders the date range passed and can reset it (ent)', async function (assert) { - this.owner.lookup('service:version').version = 'enterprise'; + this.owner.lookup('service:version').type = 'enterprise'; await this.renderComponent(); assert.dom(DATE_RANGE.dateDisplay('start')).hasText('January 2018'); @@ -75,7 +75,7 @@ module('Integration | Component | clients/date-range', function (hooks) { }); test('it renders the date range passed and cannot reset it when community', async function (assert) { - this.owner.lookup('service:version').version = 'community'; + this.owner.lookup('service:version').type = 'community'; await this.renderComponent(); assert.dom(DATE_RANGE.dateDisplay('start')).hasText('January 2018'); @@ -97,7 +97,7 @@ module('Integration | Component | clients/date-range', function (hooks) { }); test('it does not trigger onChange if date range invalid', async function (assert) { - this.owner.lookup('service:version').version = 'enterprise'; + this.owner.lookup('service:version').type = 'enterprise'; await this.renderComponent(); await click(DATE_RANGE.edit); @@ -118,7 +118,7 @@ module('Integration | Component | clients/date-range', function (hooks) { }); test('it does not trigger onChange when reset and CE', async function (assert) { - this.owner.lookup('service:version').version = 'community'; + this.owner.lookup('service:version').type = 'community'; await this.renderComponent(); await click(DATE_RANGE.edit); From 7a5c41550591559b95491daf0accc726076a1f10 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 31 Jul 2024 12:23:48 -0500 Subject: [PATCH 13/14] fix page::counts test --- .../components/clients/page/counts-test.js | 113 +++++++++++------- 1 file changed, 71 insertions(+), 42 deletions(-) diff --git a/ui/tests/integration/components/clients/page/counts-test.js b/ui/tests/integration/components/clients/page/counts-test.js index df14cf43f3c9..874de6f82c10 100644 --- a/ui/tests/integration/components/clients/page/counts-test.js +++ b/ui/tests/integration/components/clients/page/counts-test.js @@ -96,48 +96,77 @@ module('Integration | Component | clients | Page::Counts', function (hooks) { .hasText('Tracking is disabled', 'Config disabled alert renders'); }); - test('it should send correct values on start and end date change', async function (assert) { - 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')); - - const expected = { start_time: START_TIME, end_time: END_TIME }; - this.onFilterChange = (params) => { - assert.deepEqual(params, expected, 'Correct values sent on filter change'); - // in the app, the timestamp choices trigger a qp refresh as millis from epoch, - // but in the model they are translated from millis to ISO timestamps before being - // passed to this component. Mock that behavior here. - this.set( - 'startTimestamp', - params?.start_time ? fromUnixTime(params.start_time).toISOString() : START_ISO - ); - this.set('endTimestamp', params?.end_time ? fromUnixTime(params.end_time).toISOString() : END_ISO); - }; - // page starts with default billing dates, which are july 23 - jan 24 - await this.renderComponent(); - - // 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); - - // 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); - - // 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); + const jan23start = getUnixTime(new Date('2023-01-01T00:00:00Z')); + // license start is July 2, 2024 but the component defaults to beginning of the month + const july23start = getUnixTime(new Date('2023-07-01T00:00:00Z')); + const dec23end = getUnixTime(new Date('2023-12-31T00:00:00Z')); + const jan24end = getUnixTime(new Date('2024-01-31T00:00:00Z')); + [ + { + scenario: 'changing start only', + expected: { start_time: jan23start, end_time: jan24end }, + editStart: '2023-01', + expectedStart: 'January 2023', + expectedEnd: 'January 2024', + }, + { + scenario: 'changing end only', + expected: { start_time: july23start, end_time: dec23end }, + editEnd: '2023-12', + expectedStart: 'July 2023', + expectedEnd: 'December 2023', + }, + { + scenario: 'changing both', + expected: { start_time: jan23start, end_time: dec23end }, + editStart: '2023-01', + editEnd: '2023-12', + expectedStart: 'January 2023', + expectedEnd: 'December 2023', + }, + { + scenario: 'reset', + expected: { start_time: undefined, end_time: undefined }, + reset: true, + expectedStart: 'July 2023', + expectedEnd: 'January 2024', + }, + ].forEach((testCase) => { + test(`it should send correct millis value on filter change when ${testCase.scenario}`, async function (assert) { + assert.expect(5); + // set to enterprise so reset will save correctly + this.owner.lookup('service:version').type = 'enterprise'; + this.onFilterChange = (params) => { + assert.deepEqual(params, testCase.expected, 'Correct values sent on filter change'); + // in the app, the timestamp choices trigger a qp refresh as millis from epoch, + // but in the model they are translated from millis to ISO timestamps before being + // passed to this component. Mock that behavior here. + this.set( + 'startTimestamp', + params?.start_time ? fromUnixTime(params.start_time).toISOString() : START_ISO + ); + this.set('endTimestamp', params?.end_time ? fromUnixTime(params.end_time).toISOString() : END_ISO); + }; + await this.renderComponent(); + await click(CLIENT_COUNT.dateRange.edit); + + // page starts with default billing dates, which are july 23 - jan 24 + assert.dom(CLIENT_COUNT.dateRange.editDate('start')).hasValue('2023-07'); + assert.dom(CLIENT_COUNT.dateRange.editDate('end')).hasValue('2024-01'); + + if (testCase.editStart) { + await fillIn(CLIENT_COUNT.dateRange.editDate('start'), testCase.editStart); + } + if (testCase.editEnd) { + await fillIn(CLIENT_COUNT.dateRange.editDate('end'), testCase.editEnd); + } + if (testCase.reset) { + await click(CLIENT_COUNT.dateRange.reset); + } + await click(GENERAL.saveButton); + assert.dom(CLIENT_COUNT.dateRange.dateDisplay('start')).hasText(testCase.expectedStart); + assert.dom(CLIENT_COUNT.dateRange.dateDisplay('end')).hasText(testCase.expectedEnd); + }); }); test('it should render namespace and auth mount filters', async function (assert) { From b9cdc779b48ccf891e14c3f36d539804f9160c78 Mon Sep 17 00:00:00 2001 From: Chelsea Shaw Date: Wed, 31 Jul 2024 12:25:23 -0500 Subject: [PATCH 14/14] clarify comment --- ui/tests/integration/components/clients/page/counts-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/tests/integration/components/clients/page/counts-test.js b/ui/tests/integration/components/clients/page/counts-test.js index 874de6f82c10..b2567cb82632 100644 --- a/ui/tests/integration/components/clients/page/counts-test.js +++ b/ui/tests/integration/components/clients/page/counts-test.js @@ -97,7 +97,7 @@ module('Integration | Component | clients | Page::Counts', function (hooks) { }); const jan23start = getUnixTime(new Date('2023-01-01T00:00:00Z')); - // license start is July 2, 2024 but the component defaults to beginning of the month + // license start is July 2, 2024 on date change it recalculates start to beginning of the month const july23start = getUnixTime(new Date('2023-07-01T00:00:00Z')); const dec23end = getUnixTime(new Date('2023-12-31T00:00:00Z')); const jan24end = getUnixTime(new Date('2024-01-31T00:00:00Z'));