Vault Automation b057aac746
[VAULT-43339] 1/2 Chore update TS (#13050) (#13105)
* Initial ts updgrade

* Migrate linked-block to ts to squash ts errors

* [VAULT-43339] 2/2 Update vault-reporting and add ember-intl (#13062)

* Update vault-reporting and add ember-intl

* Add setupIntl for rendering tests

Co-authored-by: Jim Wright <jim.wright@hashicorp.com>
2026-03-17 15:52:40 -07:00

243 lines
11 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { click, fillIn, render, waitFor } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
import { CLIENT_COUNT } from 'vault/tests/helpers/clients/client-count-selectors';
const MOCK_DATA = [
{ island: 'Maldives', visit_length: 5, is_booked: false, trip_date: '2025-06-22T00:00:00.000Z' },
{ island: 'Bora Bora', visit_length: 7, is_booked: true, trip_date: '2025-03-15T00:00:00.000Z' },
{ island: 'Fiji', visit_length: 10, is_booked: true, trip_date: '2025-09-08T00:00:00.000Z' },
{ island: 'Santorini', visit_length: 4, is_booked: false, trip_date: '2026-04-10T00:00:00.000Z' },
{ island: 'Maui', visit_length: 8, is_booked: true, trip_date: '2026-01-18T00:00:00.000Z' },
{ island: 'Seychelles', visit_length: 6, is_booked: false, trip_date: '2025-12-03T00:00:00.000Z' },
];
module('Integration | Component | clients/table', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(async function () {
this.data = undefined;
this.columns = [
{ key: 'island', label: 'Islands', isSortable: true },
{ key: 'visit_length', label: 'Visit length', isSortable: true },
{ key: 'is_booked', label: 'Vacation booking status', isSortable: true },
{ key: 'trip_date', label: 'Date trip starts', isSortable: true },
];
this;
this.initiallySortBy = undefined;
this.setPageSize = undefined;
this.showPaginationSizeSelector = undefined;
// helper function to setup table with X number of simple objects for pagination tests
this.mockMoreData = (recordNumber) => {
const record = (i) => ({ id: i, name: `record-${i}` });
this.columns = [
{ key: 'id', label: 'ID', isSortable: true },
{ key: 'name', label: 'Name', isSortable: true },
];
this.data = Array.from({ length: recordNumber }, (_, i) => record(i));
};
this.renderComponent = async () => {
return render(hbs`
<Clients::Table
@columns={{this.columns}}
@data={{this.data}}
@initiallySortBy={{this.initiallySortBy}}
@setPageSize={{this.setPageSize}}
@showPaginationSizeSelector={{this.showPaginationSizeSelector}}
/>`);
};
});
test('it renders default empty state when no data exists', async function (assert) {
await this.renderComponent();
assert.dom(CLIENT_COUNT.card('table empty state')).hasText('No data to display');
});
test('it renders yielded empty state block when no data exists', async function (assert) {
await render(hbs`
<Clients::Table @columns={{this.columns}} @data={{this.data}}>
<:emptyState>Oh no, there's no data!</:emptyState>
</Clients::Table>`);
assert.dom(CLIENT_COUNT.card('table empty state')).hasText("Oh no, there's no data!");
});
test('it renders and paginates data', async function (assert) {
this.data = MOCK_DATA;
await this.renderComponent();
assert.dom(CLIENT_COUNT.card('table empty state')).doesNotExist();
assert.dom(GENERAL.paginationInfo).hasText(`15 of ${this.data.length}`);
await click(GENERAL.nextPage);
assert.dom(GENERAL.tableData(0, 'island')).hasText('Seychelles', 'it paginates the data');
});
test('it sorts table data', async function (assert) {
this.data = MOCK_DATA;
const assertSortOrder = (expectedValues, { column, page }) => {
expectedValues.forEach((value, idx) => {
assert
.dom(GENERAL.tableData(idx, column))
.hasText(value, `page ${page}, row ${idx} has ${column}: ${value}`);
});
};
await this.renderComponent();
const firstColumnSortButton = GENERAL.tableColumnHeaderSortButton(1, {
isAdvanced: true,
});
await click(firstColumnSortButton);
assertSortOrder(['Bora Bora', 'Fiji', 'Maldives', 'Maui', 'Santorini'], { column: 'island', page: 1 });
await click(GENERAL.nextPage);
assertSortOrder(['Seychelles'], { column: 'island', page: 2 });
await click(GENERAL.prevPage);
const secondColumnSortButton = GENERAL.tableColumnHeaderSortButton(2, {
isAdvanced: true,
});
await click(secondColumnSortButton);
assertSortOrder(['4', '5', '6', '7', '8'], { column: 'visit_length', page: 1 });
await click(GENERAL.nextPage);
assertSortOrder(['10'], { column: 'visit_length', page: 2 });
await click(GENERAL.prevPage);
const thirdColumnSortButton = GENERAL.tableColumnHeaderSortButton(3, {
isAdvanced: true,
});
await click(thirdColumnSortButton);
assertSortOrder(['false', 'false', 'false', 'true', 'true'], { column: 'is_booked', page: 1 });
await click(GENERAL.nextPage);
assertSortOrder(['true'], { column: 'is_booked', page: 2 });
await click(GENERAL.prevPage);
const fourthColumnSortButton = GENERAL.tableColumnHeaderSortButton(4, {
isAdvanced: true,
});
await click(fourthColumnSortButton);
assertSortOrder(
[
'2025-03-15T00:00:00.000Z',
'2025-06-22T00:00:00.000Z',
'2025-09-08T00:00:00.000Z',
'2025-12-03T00:00:00.000Z',
'2026-01-18T00:00:00.000Z',
],
{
column: 'trip_date',
page: 1,
}
);
await click(GENERAL.nextPage);
assertSortOrder(['2026-04-10T00:00:00.000Z'], {
column: 'trip_date',
page: 2,
});
});
test('it pre-sorts table data if @initiallySortBy is set', async function (assert) {
this.data = MOCK_DATA;
this.initiallySortBy = { column: 'visit_length', direction: 'desc' };
await this.renderComponent();
assert.dom(GENERAL.tableColumnHeader(2, { isAdvanced: true })).hasAttribute('aria-sort', 'descending');
assert
.dom(`${GENERAL.tableColumnHeader(2, { isAdvanced: true })} ${GENERAL.icon('arrow-down')}`)
.exists();
const firstPage = ['Fiji', 'Maui', 'Bora Bora', 'Seychelles', 'Maldives'];
firstPage.forEach((value, idx) => {
assert.dom(GENERAL.tableData(idx, 'island')).hasText(value, `page 1, row ${idx} has ${value}`);
});
await click(GENERAL.nextPage);
const secondPage = ['Santorini'];
secondPage.forEach((value, idx) => {
assert.dom(GENERAL.tableData(idx, 'island')).hasText(value, `page 2, row ${idx} has ${value}`);
});
});
test('it sets page size if @setPageSize has a value', async function (assert) {
this.mockMoreData(15);
this.setPageSize = 8; // component default for testing is 3, so set to anything but 3
await this.renderComponent();
assert.dom(GENERAL.paginationInfo).hasText(`1${this.setPageSize} of ${this.data.length}`);
let idx = 7; // 8th item, table items are 0-indexed
assert
.dom(GENERAL.tableData(idx, 'name'))
.hasText(this.data[idx].name, 'last row is 8th item in dataset');
await click(GENERAL.nextPage);
idx = 8; // 9th item, table items are 0-indexed
assert
.dom(GENERAL.tableData(0, 'name'))
.hasText(this.data[idx].name, 'first row on page 2 is 9th item in dataset');
});
test('it renders size selector if @showPaginationSizeSelector is true', async function (assert) {
this.mockMoreData(10);
this.setPageSize = 5;
this.showPaginationSizeSelector = true;
await this.renderComponent();
assert.dom(GENERAL.tableRow()).exists({ count: 5 }, '5 rows render');
assert.dom(GENERAL.paginationInfo).hasText(`1${this.setPageSize} of ${this.data.length}`);
assert.dom(GENERAL.paginationSizeSelector).hasValue('5');
let idx = 4; // rows and ids are 0-indexed
assert.dom(GENERAL.tableData(idx, 'id')).hasText(`${idx}`, `last row is ${idx + 1}th item in dataset`);
await fillIn(GENERAL.paginationSizeSelector, '10');
idx = 9;
assert.dom(GENERAL.tableRow()).exists({ count: 10 }, '10 rows render');
assert.dom(GENERAL.paginationSizeSelector).hasValue('10', 'it updates the size selector to 10');
assert.dom(GENERAL.tableData(idx, 'id')).hasText(`${idx}`, `last row is ${idx + 1}th item in dataset`);
});
test('it renders "Deleted" badge for "mount_type" keys if the value is "deleted mount"', async function (assert) {
this.columns = [
{ key: 'mount_type', label: 'Mount type' },
{ key: 'mount_path', label: 'Mount path' },
];
this.data = [
{ mount_type: 'deleted mount', mount_path: 'auth/userpass/' },
{ mount_type: 'ns_token', mount_path: 'auth/token/' },
];
await this.renderComponent();
assert.dom('.hds-badge').exists({ count: 1 }, 'only one badge renders');
assert
.dom(`${GENERAL.tableData(0, 'mount_type')} .hds-badge`)
.exists('it renders a badge for the deleted mount')
.hasText('Deleted');
});
test('it resets pagination when data changes', async function (assert) {
// We need more than 5 rows, so here's more mock data!
const moreData = [
{ island: 'Tahiti', visit_length: 12, is_booked: true, trip_date: '2025-05-10T00:00:00.000Z' },
{ island: 'Barbados', visit_length: 6, is_booked: false, trip_date: '2025-08-25T00:00:00.000Z' },
{ island: 'Cyprus', visit_length: 9, is_booked: true, trip_date: '2026-03-12T00:00:00.000Z' },
{ island: 'Jamaica', visit_length: 7, is_booked: false, trip_date: '2025-11-05T00:00:00.000Z' },
{ island: 'Crete', visit_length: 11, is_booked: true, trip_date: '2026-06-18T00:00:00.000Z' },
{ island: 'Aruba', visit_length: 5, is_booked: false, trip_date: '2025-10-14T00:00:00.000Z' },
];
this.data = [...MOCK_DATA, ...moreData];
this.showPaginationSizeSelector = true;
await this.renderComponent();
await fillIn(GENERAL.paginationSizeSelector, '10'); // Default is 5, so change to something else
await click(GENERAL.nextPage);
assert.dom(GENERAL.paginationInfo).hasText(`1112 of ${this.data.length}`, 'it navigates to next page');
assert.dom(GENERAL.tableRow()).exists({ count: 2 }, '2 row renders');
// Changing the @data arg should trigger an update and reset pagination
// We have to use `this.set` to trigger did-update
this.set('data', [
{ island: 'Palawan', visit_length: 9, is_booked: true, trip_date: '2025-11-14T00:00:00.000Z' },
{ island: 'Mykonos', visit_length: 3, is_booked: false, trip_date: '2026-02-28T00:00:00.000Z' },
]);
// There's a workaround using next() from @ember/runloop because the Hds::Pagination::Numbered component
// doesn't re-render when @currentPage updates. When that's fixed at the source we should be able to remove waitFor
await waitFor(GENERAL.paginationInfo);
assert.dom(GENERAL.paginationInfo).hasText(`12 of ${this.data.length}`);
assert.dom(GENERAL.tableRow()).exists({ count: 2 }, '2 rows render');
assert.dom(GENERAL.paginationSizeSelector).hasValue('10', 'page selector is unchanged when data updates');
});
});