diff --git a/ui/admin/tests/e2e/pages/workers.mjs b/ui/admin/tests/e2e/pages/workers.mjs index 7aeda8c03..9bd0e76bf 100644 --- a/ui/admin/tests/e2e/pages/workers.mjs +++ b/ui/admin/tests/e2e/pages/workers.mjs @@ -12,4 +12,60 @@ export class WorkersPage extends BaseResourcePage { await this.page.getByText('OK', { exact: true }).click(); await this.dismissSuccessAlert(); } + + /** + * Creates a new API tag on a worker + * @param {string} key API tag key + * @param {string} value API tag value + */ + async createNewTag(key, value) { + await this.page.getByRole('button', { name: 'Manage' }).click(); + await this.page.getByRole('link', { name: 'Create New Tags' }).click(); + + await this.page.getByLabel('Key').fill(key); + await this.page.getByLabel('Value').fill(value); + await this.page.getByRole('button', { name: 'Add' }).click(); + await this.page.getByRole('button', { name: 'Save' }).click(); + + await this.dismissSuccessAlert(); + } + + /** + * Edits an API tag on a worker + * @param {string} origKey Original key for API tag + * @param {string} newKey New key for API tag + * @param {string} newValue New value for API tag + */ + async editTag(origKey, newKey, newValue) { + await this.page.getByRole('table') + .getByRole('cell', { name: origKey }) + .locator('..') + .getByRole('cell', { name: 'Overflow Options' }) + .click(); + await this.page.getByRole('button', { name: 'Edit Tag' }).click(); + + await this.page.getByLabel('Key').fill(newKey); + await this.page.getByLabel('Value').fill(newValue); + await this.page.getByRole('button', { name: 'Save' }).click(); + + await this.dismissSuccessAlert(); + } + + /** + * Removes an API tag from a worker + * @param {string} key API tag key + */ + async removeTag(key) { + await this.page.getByRole('table') + .getByRole('cell', { name: key }) + .locator('..') + .getByRole('cell', { name: 'Overflow Options' }) + .click(); + await this.page.getByRole('button', { name: 'Remove Tag' }).click(); + + await this.page.getByLabel('Confirm removal').fill('REMOVE'); + await this.page.getByRole('button', { name: 'Remove' }).click(); + + await this.dismissSuccessAlert(); + } } diff --git a/ui/admin/tests/e2e/tests/worker-tags.spec.mjs b/ui/admin/tests/e2e/tests/worker-tags.spec.mjs new file mode 100644 index 000000000..09d3176a4 --- /dev/null +++ b/ui/admin/tests/e2e/tests/worker-tags.spec.mjs @@ -0,0 +1,197 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { test } from '../playwright.config.mjs' +import { expect } from '@playwright/test'; +import { customAlphabet } from 'nanoid'; + +import { authenticatedState } from '../global-setup.mjs'; +import { WorkersPage } from '../pages/workers.mjs'; + +test.use({ storageState: authenticatedState }); + +test('Worker Tags @ce @ent @aws @docker', async ({ page }) => { + const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz', 10); + + await page.goto('/'); + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Workers' }) + .click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Workers'), + ).toBeVisible(); + + // View tags from worker list page + const workerListHeadersCount = await page + .getByRole('table') + .getByRole('columnheader') + .count(); + let workerListWorkerIndex; + let workerListTagIndex; + for (let i = 0; i < workerListHeadersCount; i++) { + const header = await page + .getByRole('table') + .getByRole('columnheader') + .nth(i) + .innerText(); + if (header == 'Worker') { + workerListWorkerIndex = i; + } else if (header == 'Tags') { + workerListTagIndex = i; + } + } + await page + .getByRole('table') + .getByRole('row') + .nth(1) + .getByRole('cell') + .nth(workerListTagIndex) + .getByRole('button') + .click(); + + // Check that dialog shows a config tag + const dialogHeadersCount = await page + .getByRole('dialog') + .getByRole('table') + .getByRole('columnheader') + .count(); + let dialogTagIndex; + let dialogTypeIndex; + for (let i = 0; i < dialogHeadersCount; i++) { + const header = await page + .getByRole('dialog') + .getByRole('table') + .getByRole('columnheader') + .nth(i) + .innerText(); + if (header == 'Tag') { + dialogTagIndex = i; + } else if (header == 'Type') { + dialogTypeIndex = i; + } + } + await expect( + page + .getByRole('dialog') + .getByRole('table') + .getByRole('row') + .nth(1) + .getByRole('cell') + .nth(dialogTypeIndex) + ).toHaveText('config') + await page.getByRole('dialog').getByRole('button', { name: 'Dismiss' }).click(); + + // Go to worker details and create a new tag + await page + .getByRole('table') + .getByRole('row') + .nth(1) + .getByRole('cell') + .nth(workerListWorkerIndex) + .getByRole('link') + .click(); + const workersPage = new WorkersPage(page); + const tagKey = nanoid(); + const tagValue = nanoid(); + await workersPage.createNewTag(tagKey, tagValue); + + // Check tag in worker dialog + await page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Workers' }) + .click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Workers'), + ).toBeVisible(); + await page + .getByRole('table') + .getByRole('row') + .nth(1) + .getByRole('cell') + .nth(workerListTagIndex) + .getByRole('button') + .click(); + await expect( + page + .getByRole('dialog') + .getByRole('table') + .getByRole('cell', { name: tagKey + " = " + tagValue }) + ).toBeVisible(); + await page.getByRole('dialog').getByRole('button', { name: 'Dismiss' }).click(); + + // Go to worker details page + await page + .getByRole('table') + .getByRole('row') + .nth(1) + .getByRole('cell') + .nth(workerListWorkerIndex) + .getByRole('link') + .click(); + await page.getByRole('link', { name: 'Tags' }).click(); + const workerDetailsHeadersCount = await page + .getByRole('table') + .getByRole('columnheader') + .count(); + let workerDetailsKeyIndex; + let workerDetailsValueIndex; + let workerDetailsTypeIndex; + for (let i = 0; i < workerDetailsHeadersCount; i++) { + const header = await page + .getByRole('table') + .getByRole('columnheader') + .nth(i) + .innerText(); + if (header == 'Key') { + workerDetailsKeyIndex = i; + } else if (header == 'Value') { + workerDetailsValueIndex = i; + } else if (header == 'Type') { + workerDetailsTypeIndex = i; + } + } + + // Check tags in the worker details page + await expect( + page + .getByRole('table') + .getByRole('row') + .nth(1) + .getByRole('cell') + .nth(workerDetailsTypeIndex) + ).toHaveText('config'); + await expect( + page + .getByRole('table') + .getByRole('cell', { name: tagKey }) + .locator('..') + .getByRole('cell', { name: tagValue }) + ).toBeVisible(); + + // Edit the tag + const newTagKey = nanoid(); + const newTagValue = nanoid(); + await workersPage.editTag(tagKey, newTagKey, newTagValue); + await expect( + page + .getByRole('table') + .getByRole('cell', { name: newTagKey }) + .locator('..') + .getByRole('cell', { name: newTagValue }) + ).toBeVisible(); + + // Delete the tag + await workersPage.removeTag(newTagKey); + await expect( + page + .getByRole('table') + .getByRole('cell', { name: newTagKey }) + ).not.toBeVisible(); +});