Files
threshold-ui/e2e-tests/admin/tests/sorting.spec.js
T
hashiccandZhihe Li b42ecbff36 Add api client for playwright e2e tests (#2901)
* chore: 🤖 add @openapitools/openapi-generator-cli

* chore: 🤖 Add @nestjs/core and @openapitools/openapi-generator-cli to ignoredBuiltDependencies

These packages use postinstall for a opencollective notice

* chore: 🤖 add package.json scripts for open-api client gen

* chore: 🤖 add openapi generated files

* chore: 🤖 add boundary api client fixture

* chore: 🤖 incorporate api client fixture with base fixture

* chore: 🤖 migrate existing test to use boundary api fixture

* refactor: 💡 remove org cleanup from test

* refactor: 💡 update sorting e2e test to use api client

* refactor: 💡 clean up api client resource clean up logging

* docs: ✏️ update jsdoc links for domain clients

* chore: 🤖 avoid adding copywright headers to generated api

* Add missing copyright headers

* chore: 🤖 remove fetch argument from api client class

The hope was to use request.fetch but playwright's fetch is a
non-standard fetch which causes the generated api client to fail when
using .clone() on the response

* chore: 🤖 add optional OPENAPI_SWAGGER_URL_OR_FILE for openapi-client
generation

The OPENAPI_SWAGGER_URL_OR_FILE env var can be used to specify the
-i flag the generate:api-client script with either:
* a relative path
* an absolute path
* a url like: https://raw.githubusercontent.com/hashicorp/boundary/refs/heads/main/internal/gen/controller.swagger.json

* refactor: 💡 clean up and refactor boundary-api-client fixture

* chore: 🤖 add extra doc links and delete methods

* test: 💍 add test to ensure that api-client fixture works

* test: 💍 update existing e2e test examples with api-client

* docs: ✏️ add comment for api-client e2e test

* docs: ✏️ add additional links to service docs for api clients

* refactor: 💡 cleanup api-client and corresponding test

* docs: ✏️ add api client fixture docs to e2e README

* fix: 🐛 fix regex

* remove unnecessary capture group
* add "-" to regex to allow for url segments like "storage-buckets" in
  "/v1/storage-buckets"

* docs: ✏️ comment potential edge case in e2e api client cleanup

* docs: ✏️ fix comment typo

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>

* docs: ✏️ remove auth flag for generating e2e client

* docs: ✏️ add mention that java is required for generate:api

* chore: 🤖 implement pr feedback

* chore: 🤖 Clean up api client and add more extensive test

* chore: 🤖 fix casing in api-client test

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>

---------

Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
2025-08-12 18:08:35 -04:00

123 lines
4.2 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { test } from '../../global-setup.js';
import { RolesPage } from '../pages/roles.js';
import { expect } from '@playwright/test';
const roleNamesToCreate = ['Alpha', 'Beta', 'Delta', 'Gamma', 'Zeta'];
export const columnHeaderSortButton = (tableLocator, name) =>
tableLocator.getByRole('columnheader', { name }).getByRole('button');
export const assertColumnHeaderSortedAscending = (tableLocator, name) =>
expect(tableLocator.getByRole('columnheader', { name })).toHaveAttribute(
'aria-sort',
'ascending',
);
export const assertColumnHeaderSortedDescending = (tableLocator, name) =>
expect(tableLocator.getByRole('columnheader', { name })).toHaveAttribute(
'aria-sort',
'descending',
);
const columnHeaders = Object.freeze({
name: { index: 0, label: 'Name' },
id: { index: 2, label: 'ID' },
});
let org, rolesSortedByCreatedTime, rolesSortedByName, rolesSortedById;
test.beforeEach(async ({ apiClient }) => {
org = await apiClient.clients.Scope.scopeServiceCreateScope({
item: {
scopeId: 'global',
},
skipAdminRoleCreation: true,
skipDefaultRoleCreation: true,
});
// create roles with specific names
const roles = await Promise.all(
roleNamesToCreate.map((roleName) => {
return apiClient.clients.Role.roleServiceCreateRole({
item: {
scopeId: org.id,
name: roleName,
},
});
}),
);
// collect all roles and data so that sorting can be tested based on the underlying data
rolesSortedByCreatedTime = roles.toSorted((a, b) => {
return new Date(b.createdTime) - new Date(a.createdTime);
});
rolesSortedByName = roles.toSorted((a, b) => a.name.localeCompare(b.name));
rolesSortedById = roles.toSorted((a, b) => a.id.localeCompare(b.id));
});
test(
'Sorting works',
{ tag: ['@ce', '@ent', '@aws', '@docker'] },
async ({ page }) => {
const rolesPage = new RolesPage(page);
rolesPage.goToRolesPage({ scope: org.id });
const table = page.getByRole('table');
const tableRows = table
.getByRole('row')
.filter({ hasNot: page.getByRole('columnheader') });
await expect(tableRows).toHaveCount(roleNamesToCreate.length);
// assert that the table has the expected column headers
for (const { label, index } of Object.values(columnHeaders)) {
await expect(
table.getByRole('columnheader').nth(index).getByLabel(label),
).toBeVisible();
}
// by default, resources are sorted by created time, descending
for (const [index, role] of Object.entries(
rolesSortedByCreatedTime,
).toReversed()) {
await expect(tableRows.nth(index)).toContainText(role.name);
}
// click the "Name" column header to sort by name, ascending
await columnHeaderSortButton(table, columnHeaders.name.label).click();
await assertColumnHeaderSortedAscending(table, columnHeaders.name.label);
for (const [index, role] of Object.entries(rolesSortedByName)) {
await expect(tableRows.nth(index)).toContainText(role.name);
}
// click the "Name" column header again, to sort by name, descending
await columnHeaderSortButton(table, columnHeaders.name.label).click();
await assertColumnHeaderSortedDescending(table, columnHeaders.name.label);
for (const [index, role] of Object.entries(
rolesSortedByName.toReversed(),
)) {
await expect(tableRows.nth(index)).toContainText(role.name);
}
// click the "ID" column header to sort by ID, ascending
await columnHeaderSortButton(table, columnHeaders.id.label).click();
await assertColumnHeaderSortedAscending(table, columnHeaders.id.label);
for (const [index, role] of Object.entries(rolesSortedById)) {
await expect(tableRows.nth(index)).toContainText(role.name);
}
// click the "ID" column header again, to sort by ID, descending
await columnHeaderSortButton(table, columnHeaders.id.label).click();
await assertColumnHeaderSortedDescending(table, columnHeaders.id.label);
for (const [index, role] of Object.entries(rolesSortedById.toReversed())) {
await expect(tableRows.nth(index)).toContainText(role.name);
}
},
);