From 45c16252a72c445beef9d898365d86c9f18b4e78 Mon Sep 17 00:00:00 2001 From: Michael Li Date: Tue, 6 Aug 2024 13:15:00 -0400 Subject: [PATCH] refact(e2e): Update Admin UI tests to use Page Objects (#2426) * refact(e2e): Use page object methods * refact(e2e): Create base page class --- ui/admin/tests/e2e/helpers/boundary-ui.js | 1449 ----------------- ui/admin/tests/e2e/pages/aliases.js | 60 + ui/admin/tests/e2e/pages/auth-methods.js | 103 ++ ui/admin/tests/e2e/pages/base-resource.js | 18 + ui/admin/tests/e2e/pages/base.js | 22 + ui/admin/tests/e2e/pages/credential-stores.js | 263 +++ ui/admin/tests/e2e/pages/groups.js | 57 + ui/admin/tests/e2e/pages/host-catalogs.js | 111 ++ ui/admin/tests/e2e/pages/orgs.js | 57 + ui/admin/tests/e2e/pages/projects.js | 38 + ui/admin/tests/e2e/pages/roles.js | 77 + ui/admin/tests/e2e/pages/sessions.js | 61 + ui/admin/tests/e2e/pages/storage-buckets.js | 109 ++ ui/admin/tests/e2e/pages/storage-policies.js | 57 + ui/admin/tests/e2e/pages/targets.js | 447 +++++ ui/admin/tests/e2e/pages/users.js | 63 + ui/admin/tests/e2e/pages/workers.js | 18 + ui/admin/tests/e2e/tests/alias-ent.spec.js | 98 +- ui/admin/tests/e2e/tests/alias.spec.js | 45 +- .../tests/e2e/tests/auth-method-ldap.spec.js | 30 +- .../e2e/tests/auth-method-password.spec.js | 32 +- .../tests/credential-store-static-ent.spec.js | 65 +- .../e2e/tests/credential-store-static.spec.js | 95 +- .../e2e/tests/credential-store-vault.spec.js | 31 +- .../e2e/tests/delete-resources-ent.spec.js | 54 +- .../tests/e2e/tests/delete-resources.spec.js | 52 +- .../tests/dynamic-host-catalog-aws.spec.js | 37 +- .../tests/e2e/tests/global-settings.spec.js | 13 +- ui/admin/tests/e2e/tests/group-role.spec.js | 27 +- ui/admin/tests/e2e/tests/pagination.spec.js | 1 + .../tests/session-recording-aws-ent.spec.js | 65 +- .../tests/session-recording-minio-ent.spec.js | 65 +- ...sh-certificate-injection-vault-ent.spec.js | 38 +- ...ssh-credential-injection-vault-ent.spec.js | 45 +- ui/admin/tests/e2e/tests/target-ent.spec.js | 106 +- ui/admin/tests/e2e/tests/target.spec.js | 70 +- ui/admin/tests/e2e/tests/worker-ent.spec.js | 1 + ui/admin/tests/e2e/tests/worker.spec.js | 1 + 38 files changed, 2084 insertions(+), 1897 deletions(-) delete mode 100644 ui/admin/tests/e2e/helpers/boundary-ui.js create mode 100644 ui/admin/tests/e2e/pages/aliases.js create mode 100644 ui/admin/tests/e2e/pages/auth-methods.js create mode 100644 ui/admin/tests/e2e/pages/base-resource.js create mode 100644 ui/admin/tests/e2e/pages/base.js create mode 100644 ui/admin/tests/e2e/pages/credential-stores.js create mode 100644 ui/admin/tests/e2e/pages/groups.js create mode 100644 ui/admin/tests/e2e/pages/host-catalogs.js create mode 100644 ui/admin/tests/e2e/pages/orgs.js create mode 100644 ui/admin/tests/e2e/pages/projects.js create mode 100644 ui/admin/tests/e2e/pages/roles.js create mode 100644 ui/admin/tests/e2e/pages/sessions.js create mode 100644 ui/admin/tests/e2e/pages/storage-buckets.js create mode 100644 ui/admin/tests/e2e/pages/storage-policies.js create mode 100644 ui/admin/tests/e2e/pages/targets.js create mode 100644 ui/admin/tests/e2e/pages/users.js create mode 100644 ui/admin/tests/e2e/pages/workers.js diff --git a/ui/admin/tests/e2e/helpers/boundary-ui.js b/ui/admin/tests/e2e/helpers/boundary-ui.js deleted file mode 100644 index c0144ea12..000000000 --- a/ui/admin/tests/e2e/helpers/boundary-ui.js +++ /dev/null @@ -1,1449 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -/* eslint-disable no-undef */ -const { expect } = require('@playwright/test'); -const { readFile } = require('fs/promises'); -const { nanoid } = require('nanoid'); - -/** - * Uses the UI to create a new organization - * @param {Page} page Playwright page object - * @returns Name of the organization - */ -exports.createOrg = async (page) => { - const orgName = 'Org ' + nanoid(); - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Orgs' }) - .click(); - await page.getByRole('link', { name: 'New Org' }).click(); - await page.getByLabel('Name').fill(orgName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(orgName), - ).toBeVisible(); - - return orgName; -}; - -/** - * Uses the UI to create a new project. Assumes you have selected the desired org. - * @param {Page} page Playwright page object - * @returns Name of the project - */ -exports.createProject = async (page) => { - const projectName = 'Project ' + nanoid(); - await page - .getByRole('navigation', { name: 'General' }) - .getByRole('link', { name: 'Projects' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(projectName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(projectName), - ).toBeVisible(); - - return projectName; -}; - -/** - * Uses the UI to create a new host catalog. Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @returns Name of the host catalog - */ -exports.createHostCatalog = async (page) => { - const hostCatalogName = 'Host Catalog ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Host Catalogs' }) - .click(); - await expect( - page.getByRole('heading', { name: 'Host Catalogs' }), - ).toBeVisible(); - - const newButtonIsVisible = await page - .getByRole('link', { name: 'New', exact: true }) - .isVisible(); - if (newButtonIsVisible) { - await page.getByRole('link', { name: 'New', exact: true }).click(); - } else { - await page - .getByRole('link', { name: 'New Host Catalog', exact: true }) - .click(); - } - - await page.getByLabel('Name').fill(hostCatalogName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('group', { name: 'Type' }).getByLabel('Static').click(); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostCatalogName), - ).toBeVisible(); - - return hostCatalogName; -}; - -/** - * Uses the UI to create a new host set. Assumes you have just created a new host catalog. - * @param {Page} page Playwright page object - * @returns Name of the host set - */ -exports.createHostSet = async (page) => { - const hostSetName = 'Host Set ' + nanoid(); - await page.getByRole('link', { name: 'Host Sets' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Host Sets'), - ).toBeVisible(); - - const emptyLinkIsVisible = await page - .getByRole('link', { name: 'New', exact: true }) - .isVisible(); - if (emptyLinkIsVisible) { - await page.getByRole('link', { name: 'New', exact: true }).click(); - } else { - await page.getByText('Manage').click(); - await page.getByRole('link', { name: 'New Host Set' }).click(); - } - - await page.getByLabel('Name').fill(hostSetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostSetName), - ).toBeVisible(); - - return hostSetName; -}; - -/** - * Uses the UI to create a new host in a host set. Assumes you have just created a new host set. - * @param {Page} page Playwright page object - * @param {string} address Address of the host - * @returns Name of the host - */ -exports.createHostInHostSet = async (page, address) => { - const hostName = 'Host ' + nanoid(); - await page.getByText('Manage').click(); - await page.getByRole('link', { name: 'Create and Add Host' }).click(); - await page.getByLabel('Name').fill(hostName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByLabel('Address').fill(address); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect(page.getByRole('link', { name: hostName })).toBeVisible(); - - return hostName; -}; - -/** - * Uses the UI to create a static credential store. Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @returns Name of the credential store - */ -exports.createStaticCredentialStore = async (page) => { - const credentialStoreName = 'Credential Store ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Credential Stores' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name (Optional)').fill(credentialStoreName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('group', { name: 'Type' }).getByLabel('Static').click(); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(credentialStoreName), - ).toBeVisible(); - - return credentialStoreName; -}; - -/** - * Uses the UI to create a vault credential store. Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @param {string} vaultAddr address of the vault server - * @param {string} clientToken vault token to connect to boundary - * @returns Name of the credential store - */ -exports.createVaultCredentialStore = async (page, vaultAddr, clientToken) => { - const credentialStoreName = 'Credential Store ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Credential Stores' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name (Optional)').fill(credentialStoreName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('group', { name: 'Type' }).getByLabel('Vault').click(); - await page.getByLabel('Address').fill(vaultAddr); - await page.getByLabel('Token').fill(clientToken); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(credentialStoreName), - ).toBeVisible(); - - return credentialStoreName; -}; - -/** - * Uses the UI to create a static key pair credential. - * Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @param {string} username username for the credential - * @param {string} keyPath path to the private key - * @returns Name of the credential - */ -exports.createStaticCredentialKeyPair = async (page, username, keyPath) => { - const credentialName = 'Credential ' + nanoid(); - - const credentialsBreadcrumbIsVisible = await page - .getByRole('breadcrumbs', { name: 'Credentials' }) - .isVisible(); - if (credentialsBreadcrumbIsVisible) { - await page.getByRole('breadcrumbs', { name: 'Credentials' }).click(); - } else { - await page.getByRole('link', { name: 'Credentials', exact: true }).click(); - } - - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Credentials'), - ).toBeVisible(); - - const newButtonIsVisible = await page - .getByRole('link', { name: 'New', exact: true }) - .isVisible(); - if (newButtonIsVisible) { - await page.getByRole('link', { name: 'New', exact: true }).click(); - } else { - await page.getByText('Manage').click(); - await page.getByRole('link', { name: 'New Credential' }).click(); - } - - await page.getByLabel('Name (Optional)').fill(credentialName); - await page.getByLabel('Description').fill('This is an automated test'); - await page - .getByRole('group', { name: 'Type' }) - .getByLabel('Username & Key Pair') - .click(); - await page.getByLabel('Username', { exact: true }).fill(username); - const keyData = await readFile(keyPath, { - encoding: 'utf-8', - }); - await page.getByLabel('SSH Private Key').fill(keyData); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(credentialName), - ).toBeVisible(); - - return credentialName; -}; - -/** - * Uses the UI to create a static username and password credential. - * Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @param {string} username username for the credential - * @param {string} password password for the credential - * @returns Name of the credential - */ -exports.createStaticCredentialUsernamePassword = async ( - page, - username, - password, -) => { - const credentialsBreadcrumbIsVisible = await page - .getByRole('breadcrumbs', { name: 'Credentials' }) - .isVisible(); - if (credentialsBreadcrumbIsVisible) { - await page.getByRole('breadcrumbs', { name: 'Credentials' }).click(); - } else { - await page.getByRole('link', { name: 'Credentials', exact: true }).click(); - } - - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Credentials'), - ).toBeVisible(); - - const newButtonIsVisible = await page - .getByRole('link', { name: 'New', exact: true }) - .isVisible(); - if (newButtonIsVisible) { - await page.getByRole('link', { name: 'New', exact: true }).click(); - } else { - await page.getByText('Manage').click(); - await page.getByRole('link', { name: 'New Credential' }).click(); - } - - const credentialName = 'Credential ' + nanoid(); - await page.getByLabel('Name (Optional)').fill(credentialName); - await page.getByLabel('Description').fill('This is an automated test'); - await page - .getByRole('group', { name: 'Type' }) - .getByLabel('Username & Password') - .click(); - await page.getByLabel('Username', { exact: true }).fill(username); - await page.getByLabel('Password', { exact: true }).fill(password); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(credentialName), - ).toBeVisible(); - - return credentialName; -}; - -/** - * Uses the UI to create a vault-generic credential library. Assumes you have selected - * the desired credential store. - * @param {Page} page Playwright page object - * @param {string} vaultPath path to secret in vault - * @param {string} credentialType type of credential for credential injection - * @returns Name of the credential library - */ -exports.createVaultGenericCredentialLibrary = async ( - page, - vaultPath, - credentialType, -) => { - const credentialLibraryName = 'Credential Library ' + nanoid(); - await page.getByRole('link', { name: 'Credential Libraries' }).click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page - .getByLabel('Name (Optional)', { exact: true }) - .fill(credentialLibraryName); - await page - .getByLabel('Description (Optional)') - .fill('This is an automated test'); - await page - .getByRole('group', { name: 'Type' }) - .getByLabel('Generic Secrets') - .click(); - await page.getByLabel('Vault Path').fill(vaultPath); - await page - .getByRole('combobox', { name: 'Credential Type' }) - .selectOption(credentialType); - - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - return credentialLibraryName; -}; - -/** - * Uses the UI to create a vault-ssh-certificate credential library. Assumes you have selected - * the desired credential store. - * @param {Page} page Playwright page object - * @param {string} vaultPath path to secret in vault - * @param {string} username username for the credential - * @returns Name of the credential library - */ -exports.createVaultSshCertificateCredentialLibrary = async ( - page, - vaultPath, - username, -) => { - const credentialLibraryName = 'Credential Library ' + nanoid(); - - await page.getByRole('link', { name: 'Credential Libraries' }).click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - - await page - .getByLabel('Name (Optional)', { exact: true }) - .fill(credentialLibraryName); - await page - .getByLabel('Description (Optional)') - .fill('This is an automated test'); - await page - .getByRole('group', { name: 'Type' }) - .getByLabel('SSH Certificates') - .click(); - await page.getByLabel('Vault Path').fill(vaultPath); - await page.getByLabel('Username').fill(username); - await page.getByLabel('Key Type').selectOption('ecdsa'); - await page.getByLabel('Key Bits').fill('521'); - - await page - .getByRole('group', { name: 'Extensions' }) - .getByLabel('Key') - .fill('permity-pty'); - await page - .getByRole('group', { name: 'Extensions' }) - .getByRole('button', { name: 'Add' }) - .click(); - - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - - return credentialLibraryName; -}; - -/** - * Uses the UI to create a new target. Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @param {string} port Port of the target - * @returns Name of the target - */ -exports.createTarget = async (page, port) => { - const targetName = 'Target ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(targetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByLabel('Default Port').fill(port); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(targetName), - ).toBeVisible(); - - return targetName; -}; - -/** - * Uses the UI to create a new target with address. Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @param {string} address Address of the target - * @param {string} port Port of the target - * @returns Name of the target - */ -exports.createTargetWithAddress = async (page, address, port) => { - const targetName = 'Target ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(targetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByLabel('Target Address').fill(address); - await page.getByLabel('Default Port').fill(port); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(targetName), - ).toBeVisible(); - - return targetName; -}; - -/** - * Uses the UI to create a new target with address and alias. Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @param {string} address Address of the target - * @param {string} port Port of the target - * @param {string} alias alias used for the target - * @returns Name of the target - */ -exports.createTargetWithAddressAndAlias = async ( - page, - address, - port, - alias, -) => { - const targetName = 'Target ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(targetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByLabel('Target Address').fill(address); - await page.getByLabel('Default Port').fill(port); - await page - .getByRole('group', { name: 'Aliases' }) - .getByLabel('value') - .last() - .fill(alias); - await page.getByRole('button', { name: 'Add' }).click(); - - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(targetName), - ).toBeVisible(); - - return targetName; -}; - -/** - * Uses the UI to create a new TCP target with address in boundary-enterprise - * Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @param {string} address Address of the target - * @param {string} port Port of the target - * @returns Name of the target - */ -exports.createTcpTargetWithAddressEnt = async (page, address, port) => { - const targetName = 'Target ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(targetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('group', { name: 'Type' }).getByLabel('TCP').click(); - await page.getByLabel('Target Address').fill(address); - await page.getByLabel('Default Port').fill(port); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(targetName), - ).toBeVisible(); - - return targetName; -}; - -/** - * Uses the UI to create a new SSH target - * Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @param {string} port Port of the target - * @returns Name of the target - */ -exports.createSshTargetEnt = async (page, port) => { - const targetName = 'Target ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(targetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('group', { name: 'Type' }).getByLabel('SSH').click(); - await page.getByLabel('Default Port').fill(port); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(targetName), - ).toBeVisible(); - - return targetName; -}; - -/** - * Uses the UI to create a new SSH target with address in boundary-enterprise - * Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @param {string} address Address of the target - * @param {string} port Port of the target - * @returns Name of the target - */ -exports.createSshTargetWithAddressEnt = async (page, address, port) => { - const targetName = 'Target ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(targetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('group', { name: 'Type' }).getByLabel('SSH').click(); - await page.getByLabel('Target Address').fill(address); - await page.getByLabel('Default Port').fill(port); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(targetName), - ).toBeVisible(); - - return targetName; -}; - -/** - * Uses the UI to create a new SSH target with address and alias. - * Assumes you have selected the desired project. - * @param {Page} page Playwright page object - * @param {string} address Address of the target - * @param {string} port Port of the target - * @param {string} alias alias used for the target - * @returns Name of the target - */ -exports.createSshTargetWithAddressAndAlias = async ( - page, - address, - port, - alias, -) => { - const targetName = 'Target ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(targetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByRole('group', { name: 'Type' }).getByLabel('SSH').click(); - await page.getByLabel('Target Address').fill(address); - await page.getByLabel('Default Port').fill(port); - await page - .getByRole('group', { name: 'Aliases' }) - .getByLabel('value') - .last() - .fill(alias); - await page.getByRole('button', { name: 'Add' }).click(); - - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(targetName), - ).toBeVisible(); - - return targetName; -}; - -/** - * Uses the UI to create an alias - * @param {Page} page Playwright page object - * @param {string} alias Value of the alias - * @param {string} targetId ID of the target - * @returns Name of the alias - */ -exports.createAliasForTarget = async (page, alias, targetId) => { - const aliasName = 'Alias ' + nanoid(); - - await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - await page.getByRole('link', { name: 'Aliases' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Aliases'), - ).toBeVisible(); - - const newButtonIsVisible = await page - .getByRole('link', { name: 'Create a new alias', exact: true }) - .isVisible(); - if (newButtonIsVisible) { - await page - .getByRole('link', { name: 'Create a new alias', exact: true }) - .click(); - } else { - await page.getByRole('link', { name: 'New Alias', exact: true }).click(); - } - - await page.getByLabel('Name').fill(aliasName); - await page.getByLabel('Description').fill('This is an automated test'); - await page.getByLabel('Alias Value').fill(alias); - await page.getByLabel('Target ID').fill(targetId); - - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(aliasName), - ).toBeVisible(); - - return aliasName; -}; - -/** - * Uses the UI to delete a Boundary resource. Assume you have selected the desired resource. - * Note: For a resource to be deleted using this method, - * the resource page should allow to delete the resource using the Manage button. - * @param {Page} page Playwright page object - */ -exports.deleteResource = async (page) => { - await page.getByText('Manage').click(); - await page.getByRole('button', { name: /^(Delete|Remove Worker)/ }).click(); - await page.getByText('OK', { exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); -}; - -/** - * Uses the UI to remove auth method as primary. Assume you have selected the desired auth method. - * @param {Page} page Playwright page object - */ -exports.removeAuthMethodAsPrimary = async (page) => { - await page.getByTitle('Manage').click(); - await page.getByRole('button', { name: 'Remove as primary' }).click(); - await page.getByText('OK', { exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); -}; - -/** - * Uses the UI to add a host source to a target. Assume you have selected the desired target. - * @param {Page} page Playwright page object - * @param {string} hostSourceName Name of host source that will be attached to the target - */ -exports.addHostSourceToTarget = async (page, hostSourceName) => { - await page.getByRole('link', { name: 'Host Sources', exact: true }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Host Sources'), - ).toBeVisible(); - - const emptyLinkIsVisible = await page - .getByRole('article') - .getByRole('link', { name: 'Add Host Sources', exact: true }) - .isVisible(); - if (emptyLinkIsVisible) { - await page - .getByRole('article') - .getByRole('link', { name: 'Add Host Sources', exact: true }) - .click(); - } else { - await page.getByText('Manage').click(); - await page.getByRole('link', { name: 'Add Host Sources' }).click(); - } - - await page - .getByRole('cell', { name: hostSourceName }) - .locator('..') - .getByRole('checkbox') - .click({ force: true }); - await page.getByRole('button', { name: 'Add Host Sources' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect(page.getByRole('link', { name: hostSourceName })).toBeVisible(); -}; - -/** - * Uses the UI to add an egress worker filter to a target. Assume you have selected the desired target. - * @param {Page} page Playwright page object - * @param {string} filter Egress worker filter to be added to the target - */ -exports.addEgressWorkerFilterToTarget = async (page, filter) => { - await page.getByRole('link', { name: 'Workers', exact: true }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Workers'), - ).toBeVisible(); - - await page - .getByText('Egress workers') - .locator('..') - .getByRole('link', { name: 'Edit worker filter' }) - .click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Edit Egress Worker Filter'), - ).toBeVisible(); - - await page.getByRole('textbox').fill(filter); - - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); -}; - -/** - * Uses the UI to navigate to Sessions and waits for the session to appear - * and be in Active state. - * @param {Page} page Playwright page object - * @param {string} targetName Name of the target associated with the session - */ -exports.waitForSessionToBeVisible = async (page, targetName) => { - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Sessions' }) - .click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Sessions'), - ).toBeVisible(); - let i = 0; - let sessionIsVisible = false; - let sessionIsActive = false; - do { - i = i + 1; - sessionIsVisible = await page - .getByRole('cell', { name: targetName }) - .isVisible(); - sessionIsActive = await page - .getByRole('cell', { name: 'Active' }) - .isVisible(); - if (sessionIsVisible && sessionIsActive) { - break; - } - - const noSessionsAvailable = await page - .getByText('No Sessions Available', { exact: true }) - .isVisible(); - if (noSessionsAvailable) { - await new Promise((r) => setTimeout(r, 1000)); - await page.reload(); - } else { - await page.getByRole('button', { name: 'Refresh' }).click(); - await expect(page.getByRole('button', { name: 'Refresh' })).toBeEnabled(); - } - } while (i < 5); - - if (!sessionIsVisible) { - throw new Error('Session is not visible'); - } -}; - -/** - * Uses the UI to navigate to the specified Target and add the Brokered Credentials to it. - * @param {Page} page Playwright page object - * @param {string} targetName Name of the target associated with the session - * @param {string} credentialName Name of the credentials to be added to the target - */ -exports.addBrokeredCredentialsToTarget = async ( - page, - targetName, - credentialName, -) => { - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await expect(page.getByRole('heading', { name: 'Targets' })).toBeVisible(); - await page.getByRole('link', { name: targetName }).click(); - await page - .getByRole('link', { name: 'Brokered Credentials', exact: true }) - .click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Brokered Credentials'), - ).toBeVisible(); - - const addBrokeredCredentialsButtonIsVisible = await page - .getByRole('article') - .getByRole('link', { name: 'Add Brokered Credentials', exact: true }) - .isVisible(); - - if (addBrokeredCredentialsButtonIsVisible) { - await page - .getByRole('article') - .getByRole('link', { name: 'Add Brokered Credentials', exact: true }) - .click(); - } else { - await page.getByText('Manage').click(); - await page.getByRole('link', { name: 'Add Brokered Credentials' }).click(); - } - - await page - .getByRole('cell', { name: credentialName }) - .locator('..') - .getByRole('checkbox') - .click({ force: true }); - await page - .getByRole('button', { name: 'Add Brokered Credentials', exact: true }) - .click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect(page.getByRole('link', { name: credentialName })).toBeVisible(); -}; - -/** - * Uses the UI to navigate to the specified Target and add the Injected Credentials to it. - * @param {Page} page Playwright page object - * @param {string} targetName Name of the target associated with the session - * @param {string} credentialName Name of the credentials to be added to the target - */ -exports.addInjectedCredentialsToTarget = async ( - page, - targetName, - credentialName, -) => { - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await expect(page.getByRole('heading', { name: 'Targets' })).toBeVisible(); - await page.getByRole('link', { name: targetName }).click(); - await page - .getByRole('link', { - name: 'Injected Application Credentials', - exact: true, - }) - .click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Injected Application Credentials'), - ).toBeVisible(); - - const addInjectedCredentialsButtonIsVisible = await page - .getByRole('article') - .getByRole('link', { - name: 'Add Injected Application Credentials', - exact: true, - }) - .isVisible(); - - if (addInjectedCredentialsButtonIsVisible) { - await page - .getByRole('article') - .getByRole('link', { - name: 'Add Injected Application Credentials', - exact: true, - }) - .click(); - } else { - await page.getByText('Manage').click(); - await page.getByRole('link', { name: 'Add Injected Application' }).click(); - } - - await page - .getByRole('cell', { name: credentialName }) - .locator('..') - .getByRole('checkbox') - .click({ force: true }); - await page - .getByRole('button', { - name: 'Add Injected Application Credentials', - exact: true, - }) - .click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect(page.getByRole('link', { name: credentialName })).toBeVisible(); -}; - -/** - * Uses the UI to create new Auth Method. Assumes you have selected the desired scope. - * @param {Page} page Playwright page object - * @returns Name of the auth method - */ -exports.createPasswordAuthMethod = async (page) => { - const authMethodName = 'Auth Method ' + nanoid(); - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Auth Methods' }) - .click(); - await page.getByRole('button', { name: 'New' }).click(); - await page.getByText('Password', { exact: true }).click(); - await page.getByLabel('Name').fill(authMethodName); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(authMethodName), - ).toBeVisible(); - - return authMethodName; -}; - -/** - * Uses the UI to make the first available auth method primary. - * Assumes you have created new auth method. - * @param {Page} page Playwright page object - */ -exports.makeAuthMethodPrimary = async (page) => { - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Auth Methods' }) - .click(); - await page.getByRole('button', { name: 'Manage' }).click(); - await page.getByText('Make Primary', { exact: true }).click(); - await page.getByText('OK', { exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); -}; - -/** - * Uses the UI to create new password Account. Assumes you have selected the desired Auth Method - * which the account will be created for. - * @param {Page} page Playwright page object - * @param {string} login Login of new account - * @param {string} password Password of new account - * @returns Name of the account - */ -exports.createPasswordAccount = async (page, login, password) => { - const accountName = 'Account ' + nanoid(); - - await page.getByRole('link', { name: 'Accounts' }).click(); - await page - .getByRole('article') - .getByRole('link', { name: 'Create Account', exact: true }) - .click(); - await page.getByLabel('Name (Optional)').fill(accountName); - await page.getByLabel('Login Name').fill(login); - await page.getByLabel('Password', { exact: true }).fill(password); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(accountName), - ).toBeVisible(); - - return accountName; -}; - -/** - * Uses the UI to set a new password to an account. - * Assumes you have selected the desired Account. - * @param {Page} page Playwright page object - * @param {string} password New password of the account - */ -exports.setPasswordToAccount = async (page, password) => { - await page.getByRole('link', { name: 'Set Password' }).click(); - await page.getByLabel(new RegExp('Password*')).fill(password); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); -}; - -/** - * Uses the UI to create a new user. - * @param {Page} page Playwright page object - * @returns Name of the user - */ -exports.createUser = async (page) => { - const userName = 'User ' + nanoid(); - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Users' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(userName); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(userName), - ).toBeVisible(); - - return userName; -}; - -/** - * Uses the UI to add an account to a user - * Assumes you have selected the desired user. - * Assumes you have created new account. - * @param {Page} page Playwright page object - * @param {string} loginName Login name of the account - */ -exports.addAccountToUser = async (page, loginName) => { - await page.getByRole('link', { name: 'Accounts', exact: true }).click(); - await page - .getByRole('article') - .getByRole('link', { name: 'Add Accounts', exact: true }) - .click(); - await page - .getByRole('cell', { name: loginName }) - .locator('..') - .getByRole('checkbox') - .click({ force: true }); - - await page.getByRole('button', { name: 'Add Accounts', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); -}; - -/** - * Uses the UI to create a new group. Assumes you have selected the desired scope. - * @param {Page} page Playwright page object - * @returns Name of the group - */ -exports.createGroup = async (page) => { - const groupName = 'Group ' + nanoid(); - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Groups' }) - .click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name').fill(groupName); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(groupName), - ).toBeVisible(); - - return groupName; -}; - -/** - * Uses the UI to add a user to the group. Assumes you have selected the desired group. - * @param {Page} page Playwright page object - * @param {string} userName Name of the user that will be added to the group - */ -exports.addMemberToGroup = async (page, userName) => { - await page.getByRole('link', { name: 'Members', exact: true }).click(); - await page - .getByRole('article') - .getByRole('link', { name: 'Add Members', exact: true }) - .click(); - await page.getByRole('checkbox', { name: userName }).click(); - await page.getByRole('button', { name: 'Add Members', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect(page.getByRole('table').getByText(userName)).toBeVisible(); -}; - -/** - * Uses the UI to create a new role. Assumes you have selected the desired scope. - * @param {Page} page Playwright page object - */ -exports.createRole = async (page) => { - const roleName = 'Role ' + nanoid(); - - await page - .getByRole('navigation', { name: 'IAM' }) - .getByRole('link', { name: 'Roles' }) - .click(); - await page.getByRole('link', { name: 'New Role' }).click(); - await page.getByLabel('Name').fill(roleName); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(roleName), - ).toBeVisible(); - - return roleName; -}; - -/** - * Uses the UI to add a principal to the role. Assumes you have selected the desired role. - * @param {Page} page Playwright page object - * @param {string} principalName Name of the principal that will be added to the role - */ -exports.addPrincipalToRole = async (page, principalName) => { - await page.getByRole('link', { name: 'Principals', exact: true }).click(); - await page - .getByRole('article') - .getByRole('link', { name: 'Add Principals', exact: true }) - .click(); - await page.getByRole('checkbox', { name: principalName }).click(); - await page - .getByRole('button', { name: 'Add Principals', exact: true }) - .click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('table').getByRole('link', { name: principalName }), - ).toBeVisible(); -}; - -/** - * Uses the UI to add a grant to the role. Assumes you have selected the desired role. - * @param {Page} page Playwright page object - * @param {string} grants grants that will be given to the role - */ -exports.addGrantsToGroup = async (page, grants) => { - await page.getByRole('link', { name: 'Grants', exact: true }).click(); - await page.getByRole('textbox', { name: 'New Grant' }).fill(grants); - await page.getByRole('button', { name: 'Add', exact: true }).click(); - await page.getByRole('button', { name: 'Save', exact: true }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('textbox', { name: 'Grant', exact: true }), - ).toHaveValue(grants); -}; - -/** - * Uses the UI to create a new AWS Storage Bucket. - * @param {Page} page Playwright page object - * @param {string} scope Scope of the Storage Bucket - * @param {string} bucketName Name of the Storage Bucket - * @param {string} region Region of the Storage Bucket - * @param {string} accessKeyId Access Key ID for the Storage Bucket - * @param {string} secretAccessKey Secret Access Key for the Storage Bucket - * @param {string} workerFilter Worker filter for the Storage Bucket - * @returns Name of the Storage Bucket - */ -exports.createStorageBucketAws = async ( - page, - scope, - bucketName, - region, - accessKeyId, - secretAccessKey, - workerFilter, -) => { - const storageBucketName = 'Bucket ' + nanoid(); - await page - .getByRole('link', { name: 'Storage Buckets', exact: true }) - .click(); - await page.getByRole('link', { name: 'New Storage Bucket' }).click(); - await page.getByLabel(new RegExp('Name*')).fill(storageBucketName); - await page.getByLabel('Scope').selectOption({ label: scope }); - await page - .getByRole('group', { name: 'Provider' }) - .getByLabel('Amazon S3') - .click(); - await page.getByLabel('Bucket name').fill(bucketName); - await page.getByLabel('Region').fill(region); - await page.getByLabel('Access key ID').fill(accessKeyId); - await page.getByLabel('Secret access key').fill(secretAccessKey); - await page.getByLabel('Worker filter').fill(workerFilter); - await page.getByLabel('Disable credential rotation').click(); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(storageBucketName), - ).toBeVisible(); - - return storageBucketName; -}; - -/** - * Uses the UI to create a new MinIO Storage Bucket. - * @param {Page} page Playwright page object - * @param {string} scope Scope of the Storage Bucket - * @param {string} endpointUrl Endpoint URL of the Storage Bucket - * @param {string} bucketName Name of the Storage Bucket - * @param {string} region Region of the Storage Bucket - * @param {string} accessKeyId Access Key ID for the Storage Bucket - * @param {string} secretAccessKey Secret Access Key for the Storage Bucket - * @param {string} workerFilter Worker filter for the Storage Bucket - * @returns Name of the Storage Bucket - */ -exports.createStorageBucketMinio = async ( - page, - scope, - endpointUrl, - bucketName, - region, - accessKeyId, - secretAccessKey, - workerFilter, -) => { - const storageBucketName = 'Bucket ' + nanoid(); - await page - .getByRole('link', { name: 'Storage Buckets', exact: true }) - .click(); - await page.getByRole('link', { name: 'New Storage Bucket' }).click(); - await page.getByLabel(new RegExp('Name*')).fill(storageBucketName); - await page.getByLabel('Scope').selectOption({ label: scope }); - await page - .getByRole('group', { name: 'Provider' }) - .getByLabel('MinIO') - .click(); - await page.getByLabel('Endpoint URL').fill(endpointUrl); - await page.getByLabel('Bucket name').fill(bucketName); - await page.getByLabel('Region').fill(region); - await page.getByLabel('Access key ID').fill(accessKeyId); - await page.getByLabel('Secret access key').fill(secretAccessKey); - await page.getByLabel('Worker filter').fill(workerFilter); - await page.getByLabel('Disable credential rotation').click(); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(storageBucketName), - ).toBeVisible(); - - return storageBucketName; -}; - -/** - * Uses the UI to enable session recording for a target. Assumes you have selected the desired target. - * @param {Page} page Playwright page object - * @param {string} storageBucketName name of the Storage Bucket used for session recording - */ -exports.enableSessionRecording = async (page, storageBucketName) => { - await page.getByText('Enable recording').click(); - await page.getByLabel('Record sessions for this target').click(); - await page - .getByLabel('Storage buckets') - .selectOption({ label: storageBucketName }); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page.getByRole('listitem').getByText(storageBucketName), - ).toBeVisible(); -}; - -/** - * Uses the UI to create a Storage Policy. Assumes you have selected the desired scope. - * @param {Page} page Playwright page object - * @returns Name of the Storage Policy - */ -exports.createStoragePolicy = async (page) => { - const storagePolicyName = 'Policy ' + nanoid(); - await page - .getByRole('link', { name: 'Storage Policies', exact: true }) - .click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText('Storage Policies'), - ).toBeVisible(); - - const newButtonIsVisible = await page - .getByRole('link', { name: 'New Storage Policy', exact: true }) - .isVisible(); - if (newButtonIsVisible) { - await page - .getByRole('link', { name: 'New Storage Policy', exact: true }) - .click(); - } else { - await page - .getByRole('link', { name: 'Create a new storage policy', exact: true }) - .click(); - } - - await page.getByLabel('Name').fill(storagePolicyName); - await page.getByLabel('Retention Policy').selectOption({ label: 'Forever' }); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect( - page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(storagePolicyName), - ).toBeVisible(); - - return storagePolicyName; -}; - -/** - * Uses the UI to attach Storage Policy to a scope. Assumes you have selected the desired scope. - * @param {Page} page Playwright page object - * @param {string} policyName name of the Policy to be attached to the scope - */ -exports.attachStoragePolicy = async (page, policyName) => { - await page - .getByRole('link', { name: new RegExp('/*Settings'), exact: true }) - .click(); - await page.getByText('Add Storage Policy').click(); - await page.getByLabel('Storage Policy').selectOption({ label: policyName }); - await page.getByRole('button', { name: 'Save' }).click(); - await expect( - page.getByRole('alert').getByText('Success', { exact: true }), - ).toBeVisible(); - await page.getByRole('button', { name: 'Dismiss' }).click(); - await expect(page.getByRole('listitem').getByText(policyName)).toBeVisible(); -}; diff --git a/ui/admin/tests/e2e/pages/aliases.js b/ui/admin/tests/e2e/pages/aliases.js new file mode 100644 index 000000000..30b676607 --- /dev/null +++ b/ui/admin/tests/e2e/pages/aliases.js @@ -0,0 +1,60 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class AliasesPage extends BaseResourcePage { + /** + * Creates an alias + * @param {string} alias Value of the alias + * @param {string} targetId ID of the target + * @returns Name of the alias + */ + async createAliasForTarget(alias, targetId) { + const aliasName = 'Alias ' + nanoid(); + + await this.page.getByRole('link', { name: 'Orgs', exact: true }).click(); + await this.page.getByRole('link', { name: 'Aliases' }).click(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Aliases'), + ).toBeVisible(); + + const newButtonIsVisible = await this.page + .getByRole('link', { name: 'Create a new alias', exact: true }) + .isVisible(); + if (newButtonIsVisible) { + await this.page + .getByRole('link', { name: 'Create a new alias', exact: true }) + .click(); + } else { + await this.page + .getByRole('link', { name: 'New Alias', exact: true }) + .click(); + } + + await this.page.getByLabel('Name').fill(aliasName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page.getByLabel('Alias Value').fill(alias); + await this.page.getByLabel('Target ID').fill(targetId); + + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(aliasName), + ).toBeVisible(); + + return aliasName; + } +} + +module.exports = AliasesPage; diff --git a/ui/admin/tests/e2e/pages/auth-methods.js b/ui/admin/tests/e2e/pages/auth-methods.js new file mode 100644 index 000000000..312dd4d7d --- /dev/null +++ b/ui/admin/tests/e2e/pages/auth-methods.js @@ -0,0 +1,103 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class AuthMethodsPage extends BaseResourcePage { + /** + * Creates a new Password Auth Method. Assumes you have selected the desired scope. + * @returns Name of the auth method + */ + async createPasswordAuthMethod() { + const authMethodName = 'Auth Method ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Auth Methods' }) + .click(); + await this.page.getByRole('button', { name: 'New' }).click(); + await this.page.getByText('Password', { exact: true }).click(); + await this.page.getByLabel('Name').fill(authMethodName); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(authMethodName), + ).toBeVisible(); + + return authMethodName; + } + + /** + * Makes the first available auth method primary. + * Assumes you have created new auth method. + */ + async makeAuthMethodPrimary() { + await this.page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Auth Methods' }) + .click(); + await this.page.getByRole('button', { name: 'Manage' }).click(); + await this.page.getByText('Make Primary', { exact: true }).click(); + await this.page.getByText('OK', { exact: true }).click(); + await this.dismissSuccessAlert(); + } + + /** + * Removes auth method as primary. Assume you have selected the desired auth method. + */ + async removeAuthMethodAsPrimary() { + await this.page.getByTitle('Manage').click(); + await this.page.getByRole('button', { name: 'Remove as primary' }).click(); + await this.page.getByText('OK', { exact: true }).click(); + await this.dismissSuccessAlert(); + } + + /** + * Creates a new password Account. Assumes you have selected the desired Auth Method + * which the account will be created for. + * @param {string} login Login of new account + * @param {string} password Password of new account + * @returns Name of the account + */ + async createPasswordAccount(login, password) { + const accountName = 'Account ' + nanoid(); + + await this.page.getByRole('link', { name: 'Accounts' }).click(); + await this.page + .getByRole('article') + .getByRole('link', { name: 'Create Account', exact: true }) + .click(); + await this.page.getByLabel('Name (Optional)').fill(accountName); + await this.page.getByLabel('Login Name').fill(login); + await this.page.getByLabel('Password', { exact: true }).fill(password); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(accountName), + ).toBeVisible(); + + return accountName; + } + + /** + * Sets a new password to an account. Assumes you have selected the desired Account. + * @param {string} password New password of the account + */ + async setPasswordToAccount(password) { + await this.page.getByRole('link', { name: 'Set Password' }).click(); + await this.page.getByLabel(new RegExp('Password*')).fill(password); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + } +} + +module.exports = AuthMethodsPage; diff --git a/ui/admin/tests/e2e/pages/base-resource.js b/ui/admin/tests/e2e/pages/base-resource.js new file mode 100644 index 000000000..5e68988cb --- /dev/null +++ b/ui/admin/tests/e2e/pages/base-resource.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const BasePage = require('./base'); + +class BaseResourcePage extends BasePage { + async deleteResource() { + await this.page.getByText('Manage').click(); + await this.page.getByRole('button', { name: 'Delete' }).click(); + await this.page.getByText('OK', { exact: true }).click(); + await this.dismissSuccessAlert(); + } +} + +module.exports = BaseResourcePage; diff --git a/ui/admin/tests/e2e/pages/base.js b/ui/admin/tests/e2e/pages/base.js new file mode 100644 index 000000000..5d50321c6 --- /dev/null +++ b/ui/admin/tests/e2e/pages/base.js @@ -0,0 +1,22 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); + +class BasePage { + constructor(page) { + this.page = page; + } + + async dismissSuccessAlert() { + await expect( + this.page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await this.page.getByRole('button', { name: 'Dismiss' }).click(); + } +} + +module.exports = BasePage; diff --git a/ui/admin/tests/e2e/pages/credential-stores.js b/ui/admin/tests/e2e/pages/credential-stores.js new file mode 100644 index 000000000..f1341cc6f --- /dev/null +++ b/ui/admin/tests/e2e/pages/credential-stores.js @@ -0,0 +1,263 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); +const { readFile } = require('fs/promises'); + +const BaseResourcePage = require('./base-resource'); + +class CredentialStoresPage extends BaseResourcePage { + /** + * Creates a static credential store. Assumes you have selected the desired project. + * @returns Name of the credential store + */ + async createStaticCredentialStore() { + const credentialStoreName = 'Credential Store ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Credential Stores' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name (Optional)').fill(credentialStoreName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('Static') + .click(); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(credentialStoreName), + ).toBeVisible(); + + return credentialStoreName; + } + + /** + * Creates a vault credential store. Assumes you have selected the desired project. + * @param {string} vaultAddr address of the vault server + * @param {string} clientToken vault token to connect to boundary + * @returns Name of the credential store + */ + async createVaultCredentialStore(vaultAddr, clientToken) { + const credentialStoreName = 'Credential Store ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Credential Stores' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name (Optional)').fill(credentialStoreName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('Vault') + .click(); + await this.page.getByLabel('Address').fill(vaultAddr); + await this.page.getByLabel('Token').fill(clientToken); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(credentialStoreName), + ).toBeVisible(); + + return credentialStoreName; + } + + /** + * Creates a static key pair credential. Assumes you have selected the desired project. + * @param {string} username username for the credential + * @param {string} keyPath path to the private key + * @returns Name of the credential + */ + async createStaticCredentialKeyPair(username, keyPath) { + const credentialName = 'Credential ' + nanoid(); + + const credentialsBreadcrumbIsVisible = await this.page + .getByRole('breadcrumbs', { name: 'Credentials' }) + .isVisible(); + if (credentialsBreadcrumbIsVisible) { + await this.page.getByRole('breadcrumbs', { name: 'Credentials' }).click(); + } else { + await this.page + .getByRole('link', { name: 'Credentials', exact: true }) + .click(); + } + + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Credentials'), + ).toBeVisible(); + + const newButtonIsVisible = await this.page + .getByRole('link', { name: 'New', exact: true }) + .isVisible(); + if (newButtonIsVisible) { + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + } else { + await this.page.getByText('Manage').click(); + await this.page.getByRole('link', { name: 'New Credential' }).click(); + } + + await this.page.getByLabel('Name (Optional)').fill(credentialName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('Username & Key Pair') + .click(); + await this.page.getByLabel('Username', { exact: true }).fill(username); + const keyData = await readFile(keyPath, { + encoding: 'utf-8', + }); + await this.page.getByLabel('SSH Private Key').fill(keyData); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(credentialName), + ).toBeVisible(); + + return credentialName; + } + + /** + * Creates a static username and password credential. + * Assumes you have selected the desired project. + * @param {string} username username for the credential + * @param {string} password password for the credential + * @returns Name of the credential + */ + async createStaticCredentialUsernamePassword(username, password) { + const credentialsBreadcrumbIsVisible = await this.page + .getByRole('breadcrumbs', { name: 'Credentials' }) + .isVisible(); + if (credentialsBreadcrumbIsVisible) { + await this.page.getByRole('breadcrumbs', { name: 'Credentials' }).click(); + } else { + await this.page + .getByRole('link', { name: 'Credentials', exact: true }) + .click(); + } + + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Credentials'), + ).toBeVisible(); + + const newButtonIsVisible = await this.page + .getByRole('link', { name: 'New', exact: true }) + .isVisible(); + if (newButtonIsVisible) { + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + } else { + await this.page.getByText('Manage').click(); + await this.page.getByRole('link', { name: 'New Credential' }).click(); + } + + const credentialName = 'Credential ' + nanoid(); + await this.page.getByLabel('Name (Optional)').fill(credentialName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('Username & Password') + .click(); + await this.page.getByLabel('Username', { exact: true }).fill(username); + await this.page.getByLabel('Password', { exact: true }).fill(password); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(credentialName), + ).toBeVisible(); + + return credentialName; + } + + /** + * Creates a vault-generic credential library. Assumes you have selected + * the desired credential store. + * @param {string} vaultPath path to secret in vault + * @param {string} credentialType type of credential for credential injection + * @returns Name of the credential library + */ + async createVaultGenericCredentialLibrary(vaultPath, credentialType) { + const credentialLibraryName = 'Credential Library ' + nanoid(); + await this.page.getByRole('link', { name: 'Credential Libraries' }).click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page + .getByLabel('Name (Optional)', { exact: true }) + .fill(credentialLibraryName); + await this.page + .getByLabel('Description (Optional)') + .fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('Generic Secrets') + .click(); + await this.page.getByLabel('Vault Path').fill(vaultPath); + await this.page + .getByRole('combobox', { name: 'Credential Type' }) + .selectOption(credentialType); + + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + + return credentialLibraryName; + } + + /** + * Creates a vault-ssh-certificate credential library. Assumes you have selected + * the desired credential store. + * @param {string} vaultPath path to secret in vault + * @param {string} username username for the credential + * @returns Name of the credential library + */ + async createVaultSshCertificateCredentialLibrary(vaultPath, username) { + const credentialLibraryName = 'Credential Library ' + nanoid(); + + await this.page.getByRole('link', { name: 'Credential Libraries' }).click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + + await this.page + .getByLabel('Name (Optional)', { exact: true }) + .fill(credentialLibraryName); + await this.page + .getByLabel('Description (Optional)') + .fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('SSH Certificates') + .click(); + await this.page.getByLabel('Vault Path').fill(vaultPath); + await this.page.getByLabel('Username').fill(username); + await this.page.getByLabel('Key Type').selectOption('ecdsa'); + await this.page.getByLabel('Key Bits').fill('521'); + + await this.page + .getByRole('group', { name: 'Extensions' }) + .getByLabel('Key') + .fill('permity-pty'); + await this.page + .getByRole('group', { name: 'Extensions' }) + .getByRole('button', { name: 'Add' }) + .click(); + + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + + return credentialLibraryName; + } +} + +module.exports = CredentialStoresPage; diff --git a/ui/admin/tests/e2e/pages/groups.js b/ui/admin/tests/e2e/pages/groups.js new file mode 100644 index 000000000..0564919ad --- /dev/null +++ b/ui/admin/tests/e2e/pages/groups.js @@ -0,0 +1,57 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class GroupsPage extends BaseResourcePage { + /** + * Creates a new group. Assumes you have selected the desired scope. + * @returns Name of the group + */ + async createGroup() { + const groupName = 'Group ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Groups' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name').fill(groupName); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(groupName), + ).toBeVisible(); + + return groupName; + } + + /** + * Adds a user to the group. Assumes you have selected the desired group. + * @param {string} userName Name of the user that will be added to the group + */ + async addMemberToGroup(userName) { + await this.page.getByRole('link', { name: 'Members', exact: true }).click(); + await this.page + .getByRole('article') + .getByRole('link', { name: 'Add Members', exact: true }) + .click(); + await this.page.getByRole('checkbox', { name: userName }).click(); + await this.page + .getByRole('button', { name: 'Add Members', exact: true }) + .click(); + await this.dismissSuccessAlert(); + await expect( + this.page.getByRole('table').getByText(userName), + ).toBeVisible(); + } +} + +module.exports = GroupsPage; diff --git a/ui/admin/tests/e2e/pages/host-catalogs.js b/ui/admin/tests/e2e/pages/host-catalogs.js new file mode 100644 index 000000000..8c7b919e4 --- /dev/null +++ b/ui/admin/tests/e2e/pages/host-catalogs.js @@ -0,0 +1,111 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class HostCatalogsPage extends BaseResourcePage { + /** + * Creates a new host catalog. Assumes you have selected the desired project. + * @returns Name of the host catalog + */ + async createHostCatalog() { + const hostCatalogName = 'Host Catalog ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Host Catalogs' }) + .click(); + await expect( + this.page.getByRole('heading', { name: 'Host Catalogs' }), + ).toBeVisible(); + + const newButtonIsVisible = await this.page + .getByRole('link', { name: 'New', exact: true }) + .isVisible(); + if (newButtonIsVisible) { + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + } else { + await this.page + .getByRole('link', { name: 'New Host Catalog', exact: true }) + .click(); + } + + await this.page.getByLabel('Name').fill(hostCatalogName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('Static') + .click(); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostCatalogName), + ).toBeVisible(); + + return hostCatalogName; + } + + /** + * Creates a new host set. Assumes you have just created a new host catalog. + * @returns Name of the host set + */ + async createHostSet() { + const hostSetName = 'Host Set ' + nanoid(); + await this.page.getByRole('link', { name: 'Host Sets' }).click(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Host Sets'), + ).toBeVisible(); + + const emptyLinkIsVisible = await this.page + .getByRole('link', { name: 'New', exact: true }) + .isVisible(); + if (emptyLinkIsVisible) { + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + } else { + await this.page.getByText('Manage').click(); + await this.page.getByRole('link', { name: 'New Host Set' }).click(); + } + + await this.page.getByLabel('Name').fill(hostSetName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostSetName), + ).toBeVisible(); + + return hostSetName; + } + + /** + * Creates a new host in a host set. Assumes you have just created a new host set. + * @param {string} address Address of the host + * @returns Name of the host + */ + async createHostInHostSet(address) { + const hostName = 'Host ' + nanoid(); + await this.page.getByText('Manage').click(); + await this.page.getByRole('link', { name: 'Create and Add Host' }).click(); + await this.page.getByLabel('Name').fill(hostName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page.getByLabel('Address').fill(address); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect(this.page.getByRole('link', { name: hostName })).toBeVisible(); + + return hostName; + } +} + +module.exports = HostCatalogsPage; diff --git a/ui/admin/tests/e2e/pages/orgs.js b/ui/admin/tests/e2e/pages/orgs.js new file mode 100644 index 000000000..57b983a60 --- /dev/null +++ b/ui/admin/tests/e2e/pages/orgs.js @@ -0,0 +1,57 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class OrgsPage extends BaseResourcePage { + /** + * Creates a new organization + * @returns Name of the organization + */ + async createOrg() { + const orgName = 'Org ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Orgs' }) + .click(); + await this.page.getByRole('link', { name: 'New Org' }).click(); + await this.page.getByLabel('Name').fill(orgName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(orgName), + ).toBeVisible(); + + return orgName; + } + + /** + * Attaches a Storage Policy to a scope. Assumes you have selected the desired scope. + * @param {string} policyName name of the Policy to be attached to the scope + */ + async attachStoragePolicy(policyName) { + await this.page + .getByRole('link', { name: new RegExp('/*Settings'), exact: true }) + .click(); + await this.page.getByText('Add Storage Policy').click(); + await this.page + .getByLabel('Storage Policy') + .selectOption({ label: policyName }); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page.getByRole('listitem').getByText(policyName), + ).toBeVisible(); + } +} + +module.exports = OrgsPage; diff --git a/ui/admin/tests/e2e/pages/projects.js b/ui/admin/tests/e2e/pages/projects.js new file mode 100644 index 000000000..4d5bd86f5 --- /dev/null +++ b/ui/admin/tests/e2e/pages/projects.js @@ -0,0 +1,38 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class ProjectsPage extends BaseResourcePage { + /** + * Creates a new project. Assumes you have selected the desired org. + * @returns Name of the project + */ + async createProject() { + const projectName = 'Project ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'General' }) + .getByRole('link', { name: 'Projects' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name').fill(projectName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(projectName), + ).toBeVisible(); + + return projectName; + } +} + +module.exports = ProjectsPage; diff --git a/ui/admin/tests/e2e/pages/roles.js b/ui/admin/tests/e2e/pages/roles.js new file mode 100644 index 000000000..f14fa3b94 --- /dev/null +++ b/ui/admin/tests/e2e/pages/roles.js @@ -0,0 +1,77 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class RolesPage extends BaseResourcePage { + /** + * Creates a new role. Assumes you have selected the desired scope. + */ + async createRole() { + const roleName = 'Role ' + nanoid(); + + await this.page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Roles' }) + .click(); + await this.page.getByRole('link', { name: 'New Role' }).click(); + await this.page.getByLabel('Name').fill(roleName); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(roleName), + ).toBeVisible(); + + return roleName; + } + + /** + * Adds a principal to a role. Assumes you have selected the desired role. + * @param {string} principalName Name of the principal that will be added to the role + */ + async addPrincipalToRole(principalName) { + await this.page + .getByRole('link', { name: 'Principals', exact: true }) + .click(); + await this.page + .getByRole('article') + .getByRole('link', { name: 'Add Principals', exact: true }) + .click(); + await this.page.getByRole('checkbox', { name: principalName }).click(); + await this.page + .getByRole('button', { name: 'Add Principals', exact: true }) + .click(); + await this.dismissSuccessAlert(); + await expect( + this.page.getByRole('table').getByRole('link', { name: principalName }), + ).toBeVisible(); + } + + /** + * Adds a grant to the role. Assumes you have selected the desired role. + * @param {string} grants grants that will be given to the role + */ + async addGrantsToRole(grants) { + await this.page.getByRole('link', { name: 'Grants', exact: true }).click(); + await this.page.getByRole('textbox', { name: 'New Grant' }).fill(grants); + await this.page.getByRole('button', { name: 'Add', exact: true }).click(); + await this.page.getByRole('button', { name: 'Save', exact: true }).click(); + await expect( + this.page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await this.page.getByRole('button', { name: 'Dismiss' }).click(); + await expect( + this.page.getByRole('textbox', { name: 'Grant', exact: true }), + ).toHaveValue(grants); + } +} + +module.exports = RolesPage; diff --git a/ui/admin/tests/e2e/pages/sessions.js b/ui/admin/tests/e2e/pages/sessions.js new file mode 100644 index 000000000..782f02c3f --- /dev/null +++ b/ui/admin/tests/e2e/pages/sessions.js @@ -0,0 +1,61 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); + +const BasePage = require('./base'); + +class SessionsPage extends BasePage { + /** + * Waits for the session to appear and be in Active state. + * @param {string} targetName Name of the target associated with the session + */ + async waitForSessionToBeVisible(targetName) { + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Sessions' }) + .click(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Sessions'), + ).toBeVisible(); + let i = 0; + let sessionIsVisible = false; + let sessionIsActive = false; + do { + i = i + 1; + sessionIsVisible = await this.page + .getByRole('cell', { name: targetName }) + .isVisible(); + sessionIsActive = await this.page + .getByRole('cell', { name: 'Active' }) + .isVisible(); + if (sessionIsVisible && sessionIsActive) { + break; + } + + const noSessionsAvailable = await this.page + .getByText('No Sessions Available', { exact: true }) + .isVisible(); + if (noSessionsAvailable) { + await new Promise((r) => setTimeout(r, 1000)); + await this.page.reload(); + } else { + await this.page.getByRole('button', { name: 'Refresh' }).click(); + await expect( + this.page.getByRole('button', { name: 'Refresh' }), + ).toBeEnabled(); + } + } while (i < 5); + + if (!sessionIsVisible) { + throw new Error('Session is not visible'); + } + } +} + +module.exports = SessionsPage; diff --git a/ui/admin/tests/e2e/pages/storage-buckets.js b/ui/admin/tests/e2e/pages/storage-buckets.js new file mode 100644 index 000000000..ae2dde716 --- /dev/null +++ b/ui/admin/tests/e2e/pages/storage-buckets.js @@ -0,0 +1,109 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class StorageBucketsPage extends BaseResourcePage { + /** + * Creates a new AWS Storage Bucket. + * @param {string} scope Scope of the Storage Bucket + * @param {string} bucketName Name of the Storage Bucket + * @param {string} region Region of the Storage Bucket + * @param {string} accessKeyId Access Key ID for the Storage Bucket + * @param {string} secretAccessKey Secret Access Key for the Storage Bucket + * @param {string} workerFilter Worker filter for the Storage Bucket + * @returns Name of the Storage Bucket + */ + async createStorageBucketAws( + scope, + bucketName, + region, + accessKeyId, + secretAccessKey, + workerFilter, + ) { + const storageBucketName = 'Bucket ' + nanoid(); + await this.page + .getByRole('link', { name: 'Storage Buckets', exact: true }) + .click(); + await this.page.getByRole('link', { name: 'New Storage Bucket' }).click(); + await this.page.getByLabel(new RegExp('Name*')).fill(storageBucketName); + await this.page.getByLabel('Scope').selectOption({ label: scope }); + await this.page + .getByRole('group', { name: 'Provider' }) + .getByLabel('Amazon S3') + .click(); + await this.page.getByLabel('Bucket name').fill(bucketName); + await this.page.getByLabel('Region').fill(region); + await this.page.getByLabel('Access key ID').fill(accessKeyId); + await this.page.getByLabel('Secret access key').fill(secretAccessKey); + await this.page.getByLabel('Worker filter').fill(workerFilter); + await this.page.getByLabel('Disable credential rotation').click(); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(storageBucketName), + ).toBeVisible(); + + return storageBucketName; + } + + /** + * Creates a new MinIO Storage Bucket. + * @param {string} scope Scope of the Storage Bucket + * @param {string} endpointUrl Endpoint URL of the Storage Bucket + * @param {string} bucketName Name of the Storage Bucket + * @param {string} region Region of the Storage Bucket + * @param {string} accessKeyId Access Key ID for the Storage Bucket + * @param {string} secretAccessKey Secret Access Key for the Storage Bucket + * @param {string} workerFilter Worker filter for the Storage Bucket + * @returns Name of the Storage Bucket + */ + async createStorageBucketMinio( + scope, + endpointUrl, + bucketName, + region, + accessKeyId, + secretAccessKey, + workerFilter, + ) { + const storageBucketName = 'Bucket ' + nanoid(); + await this.page + .getByRole('link', { name: 'Storage Buckets', exact: true }) + .click(); + await this.page.getByRole('link', { name: 'New Storage Bucket' }).click(); + await this.page.getByLabel(new RegExp('Name*')).fill(storageBucketName); + await this.page.getByLabel('Scope').selectOption({ label: scope }); + await this.page + .getByRole('group', { name: 'Provider' }) + .getByLabel('MinIO') + .click(); + await this.page.getByLabel('Endpoint URL').fill(endpointUrl); + await this.page.getByLabel('Bucket name').fill(bucketName); + await this.page.getByLabel('Region').fill(region); + await this.page.getByLabel('Access key ID').fill(accessKeyId); + await this.page.getByLabel('Secret access key').fill(secretAccessKey); + await this.page.getByLabel('Worker filter').fill(workerFilter); + await this.page.getByLabel('Disable credential rotation').click(); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(storageBucketName), + ).toBeVisible(); + + return storageBucketName; + } +} + +module.exports = StorageBucketsPage; diff --git a/ui/admin/tests/e2e/pages/storage-policies.js b/ui/admin/tests/e2e/pages/storage-policies.js new file mode 100644 index 000000000..29ab9cf21 --- /dev/null +++ b/ui/admin/tests/e2e/pages/storage-policies.js @@ -0,0 +1,57 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class StoragePoliciesPage extends BaseResourcePage { + /** + * Creates a Storage Policy. Assumes you have selected the desired scope. + * @returns Name of the Storage Policy + */ + async createStoragePolicy() { + const storagePolicyName = 'Policy ' + nanoid(); + await this.page + .getByRole('link', { name: 'Storage Policies', exact: true }) + .click(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Storage Policies'), + ).toBeVisible(); + + const newButtonIsVisible = await this.page + .getByRole('link', { name: 'New Storage Policy', exact: true }) + .isVisible(); + if (newButtonIsVisible) { + await this.page + .getByRole('link', { name: 'New Storage Policy', exact: true }) + .click(); + } else { + await this.page + .getByRole('link', { name: 'Create a new storage policy', exact: true }) + .click(); + } + + await this.page.getByLabel('Name').fill(storagePolicyName); + await this.page + .getByLabel('Retention Policy') + .selectOption({ label: 'Forever' }); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(storagePolicyName), + ).toBeVisible(); + + return storagePolicyName; + } +} + +module.exports = StoragePoliciesPage; diff --git a/ui/admin/tests/e2e/pages/targets.js b/ui/admin/tests/e2e/pages/targets.js new file mode 100644 index 000000000..73bfb7d61 --- /dev/null +++ b/ui/admin/tests/e2e/pages/targets.js @@ -0,0 +1,447 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class TargetsPage extends BaseResourcePage { + /** + * Creates a new target. Assumes you have selected the desired project. + * @param {string} port Port of the target + * @returns Name of the target + */ + async createTarget(port) { + const targetName = 'Target ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name').fill(targetName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page.getByLabel('Default Port').fill(port); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(targetName), + ).toBeVisible(); + + return targetName; + } + + /** + * Creates a new target with address. Assumes you have selected the desired project. + * @param {string} address Address of the target + * @param {string} port Port of the target + * @returns Name of the target + */ + async createTargetWithAddress(address, port) { + const targetName = 'Target ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name').fill(targetName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page.getByLabel('Target Address').fill(address); + await this.page.getByLabel('Default Port').fill(port); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(targetName), + ).toBeVisible(); + + return targetName; + } + + /** + * Creates a new target with address and alias. Assumes you have selected the desired project. + * @param {string} address Address of the target + * @param {string} port Port of the target + * @param {string} alias alias used for the target + * @returns Name of the target + */ + async createTargetWithAddressAndAlias(address, port, alias) { + const targetName = 'Target ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name').fill(targetName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page.getByLabel('Target Address').fill(address); + await this.page.getByLabel('Default Port').fill(port); + await this.page + .getByRole('group', { name: 'Aliases' }) + .getByLabel('value') + .last() + .fill(alias); + await this.page.getByRole('button', { name: 'Add' }).click(); + + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(targetName), + ).toBeVisible(); + + return targetName; + } + + /** + * Creates a new TCP target with address in boundary-enterprise + * Assumes you have selected the desired project. + * @param {string} address Address of the target + * @param {string} port Port of the target + * @returns Name of the target + */ + async createTcpTargetWithAddressEnt(address, port) { + const targetName = 'Target ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name').fill(targetName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('TCP') + .click(); + await this.page.getByLabel('Target Address').fill(address); + await this.page.getByLabel('Default Port').fill(port); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(targetName), + ).toBeVisible(); + + return targetName; + } + + /** + * Creates a new SSH target. Assumes you have selected the desired project. + * @param {string} port Port of the target + * @returns Name of the target + */ + async createSshTargetEnt(port) { + const targetName = 'Target ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name').fill(targetName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('SSH') + .click(); + await this.page.getByLabel('Default Port').fill(port); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(targetName), + ).toBeVisible(); + + return targetName; + } + + /** + * Creates a new SSH target with address in boundary-enterprise + * Assumes you have selected the desired project. + * @param {string} address Address of the target + * @param {string} port Port of the target + * @returns Name of the target + */ + async createSshTargetWithAddressEnt(address, port) { + const targetName = 'Target ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name').fill(targetName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('SSH') + .click(); + await this.page.getByLabel('Target Address').fill(address); + await this.page.getByLabel('Default Port').fill(port); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(targetName), + ).toBeVisible(); + + return targetName; + } + + /** + * Creates a new SSH target with address and alias. + * Assumes you have selected the desired project. + * @param {string} address Address of the target + * @param {string} port Port of the target + * @param {string} alias alias used for the target + * @returns Name of the target + */ + async createSshTargetWithAddressAndAlias(address, port, alias) { + const targetName = 'Target ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name').fill(targetName); + await this.page.getByLabel('Description').fill('This is an automated test'); + await this.page + .getByRole('group', { name: 'Type' }) + .getByLabel('SSH') + .click(); + await this.page.getByLabel('Target Address').fill(address); + await this.page.getByLabel('Default Port').fill(port); + await this.page + .getByRole('group', { name: 'Aliases' }) + .getByLabel('value') + .last() + .fill(alias); + await this.page.getByRole('button', { name: 'Add' }).click(); + + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(targetName), + ).toBeVisible(); + + return targetName; + } + + /** + * Adds a host source to a target. Assume you have selected the desired target. + * @param {string} hostSourceName Name of host source that will be attached to the target + */ + async addHostSourceToTarget(hostSourceName) { + await this.page + .getByRole('link', { name: 'Host Sources', exact: true }) + .click(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Host Sources'), + ).toBeVisible(); + + const emptyLinkIsVisible = await this.page + .getByRole('article') + .getByRole('link', { name: 'Add Host Sources', exact: true }) + .isVisible(); + if (emptyLinkIsVisible) { + await this.page + .getByRole('article') + .getByRole('link', { name: 'Add Host Sources', exact: true }) + .click(); + } else { + await this.page.getByText('Manage').click(); + await this.page.getByRole('link', { name: 'Add Host Sources' }).click(); + } + + await this.page + .getByRole('cell', { name: hostSourceName }) + .locator('..') + .getByRole('checkbox') + .click({ force: true }); + await this.page.getByRole('button', { name: 'Add Host Sources' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page.getByRole('link', { name: hostSourceName }), + ).toBeVisible(); + } + + /** + * Adds an egress worker filter to a target. Assume you have selected the desired target. + * @param {string} filter Egress worker filter to be added to the target + */ + async addEgressWorkerFilterToTarget(filter) { + await this.page.getByRole('link', { name: 'Workers', exact: true }).click(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Workers'), + ).toBeVisible(); + + await this.page + .getByText('Egress workers') + .locator('..') + .getByRole('link', { name: 'Edit worker filter' }) + .click(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Edit Egress Worker Filter'), + ).toBeVisible(); + + await this.page.getByRole('textbox').fill(filter); + + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + } + + /** + * Adds brokered credentials to a target + * @param {string} targetName Name of the target associated with the session + * @param {string} credentialName Name of the credentials to be added to the target + */ + async addBrokeredCredentialsToTarget(targetName, credentialName) { + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await expect( + this.page.getByRole('heading', { name: 'Targets' }), + ).toBeVisible(); + await this.page.getByRole('link', { name: targetName }).click(); + await this.page + .getByRole('link', { name: 'Brokered Credentials', exact: true }) + .click(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Brokered Credentials'), + ).toBeVisible(); + + const addBrokeredCredentialsButtonIsVisible = await this.page + .getByRole('article') + .getByRole('link', { name: 'Add Brokered Credentials', exact: true }) + .isVisible(); + + if (addBrokeredCredentialsButtonIsVisible) { + await this.page + .getByRole('article') + .getByRole('link', { name: 'Add Brokered Credentials', exact: true }) + .click(); + } else { + await this.page.getByText('Manage').click(); + await this.page + .getByRole('link', { name: 'Add Brokered Credentials' }) + .click(); + } + + await this.page + .getByRole('cell', { name: credentialName }) + .locator('..') + .getByRole('checkbox') + .click({ force: true }); + await this.page + .getByRole('button', { name: 'Add Brokered Credentials', exact: true }) + .click(); + await this.dismissSuccessAlert(); + await expect( + this.page.getByRole('link', { name: credentialName }), + ).toBeVisible(); + } + + /** + * Adds injected credentials to a target + * @param {string} targetName Name of the target associated with the session + * @param {string} credentialName Name of the credentials to be added to the target + */ + async addInjectedCredentialsToTarget(targetName, credentialName) { + await this.page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await expect( + this.page.getByRole('heading', { name: 'Targets' }), + ).toBeVisible(); + await this.page.getByRole('link', { name: targetName }).click(); + await this.page + .getByRole('link', { + name: 'Injected Application Credentials', + exact: true, + }) + .click(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Injected Application Credentials'), + ).toBeVisible(); + + const addInjectedCredentialsButtonIsVisible = await this.page + .getByRole('article') + .getByRole('link', { + name: 'Add Injected Application Credentials', + exact: true, + }) + .isVisible(); + + if (addInjectedCredentialsButtonIsVisible) { + await this.page + .getByRole('article') + .getByRole('link', { + name: 'Add Injected Application Credentials', + exact: true, + }) + .click(); + } else { + await this.page.getByText('Manage').click(); + await this.page + .getByRole('link', { name: 'Add Injected Application' }) + .click(); + } + + await this.page + .getByRole('cell', { name: credentialName }) + .locator('..') + .getByRole('checkbox') + .click({ force: true }); + await this.page + .getByRole('button', { + name: 'Add Injected Application Credentials', + exact: true, + }) + .click(); + await this.dismissSuccessAlert(); + await expect( + this.page.getByRole('link', { name: credentialName }), + ).toBeVisible(); + } + + /** + * Enables session recording for a target. Assumes you have selected the desired target. + * @param {string} storageBucketName name of the Storage Bucket used for session recording + */ + async enableSessionRecording(storageBucketName) { + await this.page.getByText('Enable recording').click(); + await this.page.getByLabel('Record sessions for this target').click(); + await this.page + .getByLabel('Storage buckets') + .selectOption({ label: storageBucketName }); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page.getByRole('listitem').getByText(storageBucketName), + ).toBeVisible(); + } +} + +module.exports = TargetsPage; diff --git a/ui/admin/tests/e2e/pages/users.js b/ui/admin/tests/e2e/pages/users.js new file mode 100644 index 000000000..0fb0f9d23 --- /dev/null +++ b/ui/admin/tests/e2e/pages/users.js @@ -0,0 +1,63 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const { expect } = require('@playwright/test'); +const { nanoid } = require('nanoid'); + +const BaseResourcePage = require('./base-resource'); + +class UsersPage extends BaseResourcePage { + /** + * Creates a new user. + * @returns Name of the user + */ + async createUser() { + const userName = 'User ' + nanoid(); + await this.page + .getByRole('navigation', { name: 'IAM' }) + .getByRole('link', { name: 'Users' }) + .click(); + await this.page.getByRole('link', { name: 'New', exact: true }).click(); + await this.page.getByLabel('Name').fill(userName); + await this.page.getByRole('button', { name: 'Save' }).click(); + await this.dismissSuccessAlert(); + await expect( + this.page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(userName), + ).toBeVisible(); + + return userName; + } + + /** + * Adds an account to a user + * Assumes you have selected the desired user. + * Assumes you have created new account. + * @param {string} loginName Login name of the account + */ + async addAccountToUser(loginName) { + await this.page + .getByRole('link', { name: 'Accounts', exact: true }) + .click(); + await this.page + .getByRole('article') + .getByRole('link', { name: 'Add Accounts', exact: true }) + .click(); + await this.page + .getByRole('cell', { name: loginName }) + .locator('..') + .getByRole('checkbox') + .click({ force: true }); + + await this.page + .getByRole('button', { name: 'Add Accounts', exact: true }) + .click(); + await this.dismissSuccessAlert(); + } +} + +module.exports = UsersPage; diff --git a/ui/admin/tests/e2e/pages/workers.js b/ui/admin/tests/e2e/pages/workers.js new file mode 100644 index 000000000..0776fa65c --- /dev/null +++ b/ui/admin/tests/e2e/pages/workers.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +/* eslint-disable no-undef */ +const BaseResourcePage = require('./base-resource'); + +class WorkersPage extends BaseResourcePage { + async deleteResource() { + await this.page.getByText('Manage').click(); + await this.page.getByRole('button', { name: 'Remove Worker' }).click(); + await this.page.getByText('OK', { exact: true }).click(); + await this.dismissSuccessAlert(); + } +} + +module.exports = WorkersPage; diff --git a/ui/admin/tests/e2e/tests/alias-ent.spec.js b/ui/admin/tests/e2e/tests/alias-ent.spec.js index 83cf88121..ff9f09acf 100644 --- a/ui/admin/tests/e2e/tests/alias-ent.spec.js +++ b/ui/admin/tests/e2e/tests/alias-ent.spec.js @@ -7,6 +7,7 @@ const { test, expect } = require('@playwright/test'); import { execSync } from 'child_process'; import { customAlphabet } from 'nanoid'; + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, @@ -15,16 +16,11 @@ const { deleteAliasCli, deleteOrgCli, } = require('../helpers/boundary-cli'); -const { - addInjectedCredentialsToTarget, - createAliasForTarget, - createOrg, - createProject, - createSshTargetWithAddressEnt, - createStaticCredentialKeyPair, - createStaticCredentialStore, - createTargetWithAddressAndAlias, -} = require('../helpers/boundary-ui'); +const AliasesPage = require('../pages/aliases'); +const CredentialStoresPage = require('../pages/credential-stores'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const TargetsPage = require('../pages/targets'); test.use({ storageState: authenticatedState }); @@ -48,20 +44,27 @@ test.describe('Aliases (Enterprise)', async () => { let alias; const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); try { - orgName = await createOrg(page); - await createProject(page); - const targetName = await createSshTargetWithAddressEnt( - page, + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); + + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); - await createStaticCredentialStore(page); - const credentialName = await createStaticCredentialKeyPair( - page, - process.env.E2E_SSH_USER, - process.env.E2E_SSH_KEY_PATH, + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + process.env.E2E_SSH_USER, + process.env.E2E_SSH_KEY_PATH, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await addInjectedCredentialsToTarget(page, targetName, credentialName); // Create alias for target await page @@ -126,28 +129,34 @@ test.describe('Aliases (Enterprise)', async () => { let alias; const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); try { - orgName = await createOrg(page); + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, process.env.E2E_PASSWORD_AUTH_METHOD_ID, process.env.E2E_PASSWORD_ADMIN_LOGIN_NAME, process.env.E2E_PASSWORD_ADMIN_PASSWORD, ); - await createProject(page); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); alias = 'example.alias.' + nanoid(); - const targetName = await createTargetWithAddressAndAlias( - page, + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTargetWithAddressAndAlias( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, alias, ); - await createStaticCredentialStore(page); - const credentialName = await createStaticCredentialKeyPair( - page, - process.env.E2E_SSH_USER, - process.env.E2E_SSH_KEY_PATH, + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + process.env.E2E_SSH_USER, + process.env.E2E_SSH_KEY_PATH, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await addInjectedCredentialsToTarget(page, targetName, credentialName); // Connect to target using alias await authorizeAlias(alias); @@ -178,20 +187,26 @@ test.describe('Aliases (Enterprise)', async () => { let org; let alias; try { - const orgName = await createOrg(page); - const projectName = await createProject(page); - const targetName = await createSshTargetWithAddressEnt( - page, + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); - await createStaticCredentialStore(page); - const credentialName = await createStaticCredentialKeyPair( - page, - process.env.E2E_SSH_USER, - process.env.E2E_SSH_KEY_PATH, + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + process.env.E2E_SSH_USER, + process.env.E2E_SSH_KEY_PATH, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await addInjectedCredentialsToTarget(page, targetName, credentialName); // Create new alias from scope page await authenticateBoundaryCli( @@ -215,7 +230,8 @@ test.describe('Aliases (Enterprise)', async () => { const target = targets.items.filter((obj) => obj.name == targetName)[0]; alias = 'example.alias.' + nanoid(); - await createAliasForTarget(page, alias, target.id); + const aliasesPage = new AliasesPage(page); + await aliasesPage.createAliasForTarget(alias, target.id); await authorizeAlias(alias); } finally { await authenticateBoundaryCli( diff --git a/ui/admin/tests/e2e/tests/alias.spec.js b/ui/admin/tests/e2e/tests/alias.spec.js index 7e0700cfd..98d2ad296 100644 --- a/ui/admin/tests/e2e/tests/alias.spec.js +++ b/ui/admin/tests/e2e/tests/alias.spec.js @@ -7,6 +7,7 @@ const { test, expect } = require('@playwright/test'); import { execSync } from 'child_process'; import { customAlphabet } from 'nanoid'; + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, @@ -15,13 +16,10 @@ const { deleteAliasCli, deleteOrgCli, } = require('../helpers/boundary-cli'); -const { - createAliasForTarget, - createOrg, - createProject, - createTargetWithAddress, - createTargetWithAddressAndAlias, -} = require('../helpers/boundary-ui'); +const AliasesPage = require('../pages/aliases'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const TargetsPage = require('../pages/targets'); test.use({ storageState: authenticatedState }); @@ -45,10 +43,12 @@ test.describe('Aliases', async () => { let alias; const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); try { - orgName = await createOrg(page); - await createProject(page); - await createTargetWithAddress( - page, + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + await targetsPage.createTargetWithAddress( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); @@ -112,17 +112,19 @@ test.describe('Aliases', async () => { let alias; const nanoid = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 10); try { - orgName = await createOrg(page); + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, process.env.E2E_PASSWORD_AUTH_METHOD_ID, process.env.E2E_PASSWORD_ADMIN_LOGIN_NAME, process.env.E2E_PASSWORD_ADMIN_PASSWORD, ); - await createProject(page); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); alias = 'example.alias.' + nanoid(); - await createTargetWithAddressAndAlias( - page, + const targetsPage = new TargetsPage(page); + await targetsPage.createTargetWithAddressAndAlias( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, alias, @@ -157,10 +159,12 @@ test.describe('Aliases', async () => { let org; let alias; try { - const orgName = await createOrg(page); - const projectName = await createProject(page); - const targetName = await createTargetWithAddress( - page, + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTargetWithAddress( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); @@ -187,7 +191,8 @@ test.describe('Aliases', async () => { const target = targets.items.filter((obj) => obj.name == targetName)[0]; alias = 'example.alias.' + nanoid(); - await createAliasForTarget(page, alias, target.id); + const aliasesPage = new AliasesPage(page); + await aliasesPage.createAliasForTarget(alias, target.id); await authorizeAlias(alias); } finally { await authenticateBoundaryCli( diff --git a/ui/admin/tests/e2e/tests/auth-method-ldap.spec.js b/ui/admin/tests/e2e/tests/auth-method-ldap.spec.js index 141bbf261..2ff984065 100644 --- a/ui/admin/tests/e2e/tests/auth-method-ldap.spec.js +++ b/ui/admin/tests/e2e/tests/auth-method-ldap.spec.js @@ -6,22 +6,18 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); const { execSync } = require('child_process'); -const { checkEnv } = require('../helpers/general'); const { nanoid } = require('nanoid'); +const { checkEnv } = require('../helpers/general'); const { authenticateBoundaryCli, checkBoundaryCli, deleteOrgCli, } = require('../helpers/boundary-cli'); -const { - addAccountToUser, - createPasswordAuthMethod, - createOrg, - createRole, - createUser, - addPrincipalToRole, -} = require('../helpers/boundary-ui'); +const AuthMethodsPage = require('../pages/auth-methods'); +const OrgsPage = require('../pages/orgs'); +const RolesPage = require('../pages/roles'); +const UsersPage = require('../pages/users'); test.beforeAll(async () => { await checkEnv([ @@ -60,7 +56,8 @@ test('Set up LDAP auth method @ce @ent @docker', async ({ page }) => { ).toBeVisible(); // Create an LDAP auth method - orgName = await createOrg(page); + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); await page .getByRole('navigation', { name: 'IAM' }) .getByRole('link', { name: 'Auth Methods' }) @@ -168,16 +165,19 @@ test('Set up LDAP auth method @ce @ent @docker', async ({ page }) => { await page.getByRole('button', { name: 'Dismiss' }).click(); // Create a role and add LDAP managed group to role - await createRole(page); - await addPrincipalToRole(page, ldapManagedGroupName); + const rolesPage = new RolesPage(page); + await rolesPage.createRole(); + await rolesPage.addPrincipalToRole(ldapManagedGroupName); // Create a user and attach LDAP account to it - const userName = await createUser(page); - await addAccountToUser(page, process.env.E2E_LDAP_USER_NAME); + const usersPage = new UsersPage(page); + const userName = await usersPage.createUser(); + await usersPage.addAccountToUser(process.env.E2E_LDAP_USER_NAME); // Create a second auth method so that there's multiple auth methods on the // login screen - await createPasswordAuthMethod(page); + const authMethodsPage = new AuthMethodsPage(page); + await authMethodsPage.createPasswordAuthMethod(); // Log out await page.getByText(process.env.E2E_PASSWORD_ADMIN_LOGIN_NAME).click(); diff --git a/ui/admin/tests/e2e/tests/auth-method-password.spec.js b/ui/admin/tests/e2e/tests/auth-method-password.spec.js index 4f111046e..a7a9bce25 100644 --- a/ui/admin/tests/e2e/tests/auth-method-password.spec.js +++ b/ui/admin/tests/e2e/tests/auth-method-password.spec.js @@ -6,20 +6,15 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); const { execSync } = require('child_process'); + const { authenticateBoundaryCli, checkBoundaryCli, deleteOrgCli, } = require('../helpers/boundary-cli'); -const { - createOrg, - createPasswordAuthMethod, - createPasswordAccount, - setPasswordToAccount, - createUser, - addAccountToUser, - makeAuthMethodPrimary, -} = require('../helpers/boundary-ui'); +const AuthMethodsPage = require('../pages/auth-methods'); +const OrgsPage = require('../pages/orgs'); +const UsersPage = require('../pages/users'); test.beforeAll(async () => { await checkBoundaryCli(); @@ -51,14 +46,19 @@ test('Verify new auth-method can be created and assigned to users @ce @ent @aws ).toBeVisible(); // Create a new password auth method and account - orgName = await createOrg(page); - authMethodName = await createPasswordAuthMethod(page); + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + + const authMethodsPage = new AuthMethodsPage(page); + authMethodName = await authMethodsPage.createPasswordAuthMethod(); const username = 'test-user'; const password = 'password'; - await createPasswordAccount(page, username, password); - await makeAuthMethodPrimary(page); - await createUser(page); - await addAccountToUser(page, username); + + await authMethodsPage.createPasswordAccount(username, password); + await authMethodsPage.makeAuthMethodPrimary(); + const usersPage = new UsersPage(page); + await usersPage.createUser(); + await usersPage.addAccountToUser(username); // Log out await page.getByText(process.env.E2E_PASSWORD_ADMIN_LOGIN_NAME).click(); @@ -113,7 +113,7 @@ test('Verify new auth-method can be created and assigned to users @ce @ent @aws await page.getByRole('link', { name: 'Accounts', exact: true }).click(); await page.getByRole('link', { name: username }).click(); const newPassword = 'new-password'; - await setPasswordToAccount(page, newPassword); + await authMethodsPage.setPasswordToAccount(newPassword); // Log out await page.getByText(process.env.E2E_PASSWORD_ADMIN_LOGIN_NAME).click(); diff --git a/ui/admin/tests/e2e/tests/credential-store-static-ent.spec.js b/ui/admin/tests/e2e/tests/credential-store-static-ent.spec.js index f91dc542d..999423f88 100644 --- a/ui/admin/tests/e2e/tests/credential-store-static-ent.spec.js +++ b/ui/admin/tests/e2e/tests/credential-store-static-ent.spec.js @@ -6,22 +6,17 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); const { execSync } = require('child_process'); + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, checkBoundaryCli, deleteOrgCli, } = require('../helpers/boundary-cli'); -const { - addBrokeredCredentialsToTarget, - addInjectedCredentialsToTarget, - createOrg, - createProject, - createSshTargetWithAddressEnt, - createStaticCredentialKeyPair, - createStaticCredentialStore, - createStaticCredentialUsernamePassword, -} = require('../helpers/boundary-ui'); +const CredentialStoresPage = require('../pages/credential-stores'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const TargetsPage = require('../pages/targets'); test.use({ storageState: authenticatedState }); @@ -41,29 +36,37 @@ test('Multiple Credential Stores (ENT) @ent @aws @docker', async ({ page }) => { try { await page.goto('/'); - orgName = await createOrg(page); - await createProject(page); - const targetName = await createSshTargetWithAddressEnt( - page, + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); - await createStaticCredentialStore(page); + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + process.env.E2E_SSH_USER, + process.env.E2E_SSH_KEY_PATH, + ); + const credentialName2 = + await credentialStoresPage.createStaticCredentialUsernamePassword( + process.env.E2E_SSH_USER, + 'testPassword', + ); - const credentialName = await createStaticCredentialKeyPair( - page, - process.env.E2E_SSH_USER, - process.env.E2E_SSH_KEY_PATH, + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, ); - const credentialName2 = await createStaticCredentialUsernamePassword( - page, - process.env.E2E_SSH_USER, - 'testPassword', + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName2, ); - await addBrokeredCredentialsToTarget(page, targetName, credentialName); - await addBrokeredCredentialsToTarget(page, targetName, credentialName2); - // Remove a credential from the target await page .getByRole('link', { name: credentialName2 }) @@ -78,8 +81,14 @@ test('Multiple Credential Stores (ENT) @ent @aws @docker', async ({ page }) => { ).toBeVisible(); await page.getByRole('button', { name: 'Dismiss' }).click(); - await addInjectedCredentialsToTarget(page, targetName, credentialName); - await addInjectedCredentialsToTarget(page, targetName, credentialName2); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName2, + ); // Remove a credential from the target await page diff --git a/ui/admin/tests/e2e/tests/credential-store-static.spec.js b/ui/admin/tests/e2e/tests/credential-store-static.spec.js index 8f67490bd..612d8a746 100644 --- a/ui/admin/tests/e2e/tests/credential-store-static.spec.js +++ b/ui/admin/tests/e2e/tests/credential-store-static.spec.js @@ -8,6 +8,7 @@ const { test, expect } = require('@playwright/test'); const { execSync } = require('child_process'); const { readFile } = require('fs/promises'); const { nanoid } = require('nanoid'); + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, @@ -15,15 +16,10 @@ const { deleteOrgCli, getSessionCli, } = require('../helpers/boundary-cli'); -const { - addBrokeredCredentialsToTarget, - createOrg, - createProject, - createTargetWithAddress, - createStaticCredentialStore, - createStaticCredentialKeyPair, - createStaticCredentialUsernamePassword, -} = require('../helpers/boundary-ui'); +const CredentialStoresPage = require('../pages/credential-stores'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const TargetsPage = require('../pages/targets'); test.use({ storageState: authenticatedState }); @@ -45,26 +41,34 @@ let targetName; test.beforeEach(async ({ page }) => { await page.goto('/'); - orgName = await createOrg(page); - projectName = await createProject(page); - targetName = await createTargetWithAddress( - page, + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + targetName = await targetsPage.createTargetWithAddress( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); - await createStaticCredentialStore(page); + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); }); test('Static Credential Store (User & Key Pair) @ce @aws @docker', async ({ page, }) => { try { - const credentialName = await createStaticCredentialKeyPair( - page, - process.env.E2E_SSH_USER, - process.env.E2E_SSH_KEY_PATH, + const credentialStoresPage = new CredentialStoresPage(page); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + process.env.E2E_SSH_USER, + process.env.E2E_SSH_KEY_PATH, + ); + const targetsPage = new TargetsPage(page); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, ); - await addBrokeredCredentialsToTarget(page, targetName, credentialName); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, @@ -106,12 +110,17 @@ test('Static Credential Store (Username & Password) @ce @aws @docker', async ({ }) => { try { const testPassword = 'password'; - const credentialName = await createStaticCredentialUsernamePassword( - page, - process.env.E2E_SSH_USER, - testPassword, + const credentialStoresPage = new CredentialStoresPage(page); + const credentialName = + await credentialStoresPage.createStaticCredentialUsernamePassword( + process.env.E2E_SSH_USER, + testPassword, + ); + const targetsPage = new TargetsPage(page); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, ); - await addBrokeredCredentialsToTarget(page, targetName, credentialName); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, @@ -170,7 +179,11 @@ test('Static Credential Store (JSON) @ce @aws @docker', async ({ page }) => { .getByText(credentialName), ).toBeVisible(); - await addBrokeredCredentialsToTarget(page, targetName, credentialName); + const targetsPage = new TargetsPage(page); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, + ); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, @@ -205,19 +218,27 @@ test('Static Credential Store (JSON) @ce @aws @docker', async ({ page }) => { test('Multiple Credential Stores (CE) @ce @aws @docker', async ({ page }) => { try { - const credentialName = await createStaticCredentialKeyPair( - page, - process.env.E2E_SSH_USER, - process.env.E2E_SSH_KEY_PATH, - ); - const credentialName2 = await createStaticCredentialUsernamePassword( - page, - process.env.E2E_SSH_USER, - 'testPassword', - ); + const credentialStoresPage = new CredentialStoresPage(page); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + process.env.E2E_SSH_USER, + process.env.E2E_SSH_KEY_PATH, + ); + const credentialName2 = + await credentialStoresPage.createStaticCredentialUsernamePassword( + process.env.E2E_SSH_USER, + 'testPassword', + ); - await addBrokeredCredentialsToTarget(page, targetName, credentialName); - await addBrokeredCredentialsToTarget(page, targetName, credentialName2); + const targetsPage = new TargetsPage(page); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName, + ); + await targetsPage.addBrokeredCredentialsToTarget( + targetName, + credentialName2, + ); // Remove the host source from the target await page diff --git a/ui/admin/tests/e2e/tests/credential-store-vault.spec.js b/ui/admin/tests/e2e/tests/credential-store-vault.spec.js index 5fa496a72..ed2bc58eb 100644 --- a/ui/admin/tests/e2e/tests/credential-store-vault.spec.js +++ b/ui/admin/tests/e2e/tests/credential-store-vault.spec.js @@ -7,6 +7,8 @@ const { test, expect } = require('@playwright/test'); const { execSync } = require('child_process'); const { nanoid } = require('nanoid'); +const { readFile } = require('fs/promises'); + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, @@ -14,14 +16,10 @@ const { deleteOrgCli, } = require('../helpers/boundary-cli'); const { checkVaultCli } = require('../helpers/vault-cli'); -const { - createOrg, - createProject, - createVaultCredentialStore, - createTargetWithAddress, - addBrokeredCredentialsToTarget, -} = require('../helpers/boundary-ui'); -const { readFile } = require('fs/promises'); +const CredentialStoresPage = require('../pages/credential-stores'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const TargetsPage = require('../pages/targets'); const secretsPath = 'e2e_secrets'; const secretName = 'cred'; @@ -83,7 +81,8 @@ test('Vault Credential Store (User & Key Pair) @ce @aws @docker', async ({ ); const clientToken = vaultToken.auth.client_token; - const orgName = await createOrg(page); + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, process.env.E2E_PASSWORD_AUTH_METHOD_ID, @@ -93,14 +92,15 @@ test('Vault Credential Store (User & Key Pair) @ce @aws @docker', async ({ const orgs = JSON.parse(execSync('boundary scopes list -format json')); org = orgs.items.filter((obj) => obj.name == orgName)[0]; - const projectName = await createProject(page); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); const projects = JSON.parse( execSync(`boundary scopes list -format json -scope-id ${org.id}`), ); const project = projects.items.filter((obj) => obj.name == projectName)[0]; - const targetName = await createTargetWithAddress( - page, + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTargetWithAddress( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); @@ -109,8 +109,8 @@ test('Vault Credential Store (User & Key Pair) @ce @aws @docker', async ({ ); const target = targets.items.filter((obj) => obj.name == targetName)[0]; - await createVaultCredentialStore( - page, + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createVaultCredentialStore( process.env.E2E_VAULT_ADDR, clientToken, ); @@ -133,8 +133,7 @@ test('Vault Credential Store (User & Key Pair) @ce @aws @docker', async ({ ).toBeVisible(); await page.getByRole('button', { name: 'Dismiss' }).click(); - await addBrokeredCredentialsToTarget( - page, + await targetsPage.addBrokeredCredentialsToTarget( targetName, credentialLibraryName, ); diff --git a/ui/admin/tests/e2e/tests/delete-resources-ent.spec.js b/ui/admin/tests/e2e/tests/delete-resources-ent.spec.js index 7353202d3..f3e3ddc2a 100644 --- a/ui/admin/tests/e2e/tests/delete-resources-ent.spec.js +++ b/ui/admin/tests/e2e/tests/delete-resources-ent.spec.js @@ -5,6 +5,7 @@ /* eslint-disable no-undef */ const { test } = require('@playwright/test'); +const { execSync } = require('child_process'); const { authenticatedState, checkEnv } = require('../helpers/general'); const { @@ -30,13 +31,10 @@ const { createUsernamePasswordCredentialCli, deleteOrgCli, } = require('../helpers/boundary-cli'); - -const { - deleteResource, - removeAuthMethodAsPrimary, -} = require('../helpers/boundary-ui'); const { checkVaultCli } = require('../helpers/vault-cli'); -const { execSync } = require('child_process'); +const AuthMethodsPage = require('../pages/auth-methods'); +const BaseResourcePage = require('../pages/base-resource'); +const WorkersPage = require('../pages/workers'); const secretPolicyName = 'kv-policy'; const boundaryPolicyName = 'boundary-controller'; @@ -112,65 +110,69 @@ test('Verify resources can be deleted (enterprise) @ent @aws', async ({ let sshTargetId = await createSshTargetCli(projectId); // Delete resources + const baseResourcePage = new BaseResourcePage(page); + await page.goto(`/scopes/${projectId}/targets/${tcpTargetId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto( `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}/credentials/${usernamePasswordCredentialId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto( `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto( `/scopes/${projectId}/credential-stores/${vaultCredentialStoreId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto( `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/host-sets/${staticHostSetId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto( `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/hosts/${staticHostId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto( `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto( `/scopes/${projectId}/host-catalogs/${dynamicAwsHostCatalogId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto(`/scopes/${projectId}/users/${userId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto(`/scopes/${projectId}/groups/${groupId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto(`/scopes/global/roles/${globalScopeRoleId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto(`/scopes/${projectId}/roles/${orgScopeRoleId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto(`/scopes/${projectId}/roles/${projectScopeRoleId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto( `/scopes/${orgId}/auth-methods/${authMethodId}/accounts/${passwordAccountId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto(`/scopes/${orgId}/auth-methods/${authMethodId}`); - await removeAuthMethodAsPrimary(page); - await deleteResource(page); + const authMethodsPage = new AuthMethodsPage(page); + await authMethodsPage.removeAuthMethodAsPrimary(); + await baseResourcePage.deleteResource(page); await page.goto(`/scopes/global/workers/${workerId}`); - await deleteResource(page); + const workersPage = new WorkersPage(page); + await workersPage.deleteResource(page); // Delete enterprise resources await page.goto(`/scopes/${projectId}/targets/${sshTargetId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete project and org await page.goto(`/scopes/${projectId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); await page.goto(`/scopes/${orgId}/edit`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); orgDeleted = true; } finally { if (orgId && orgDeleted == false) { diff --git a/ui/admin/tests/e2e/tests/delete-resources.spec.js b/ui/admin/tests/e2e/tests/delete-resources.spec.js index f04273ff3..64979235e 100644 --- a/ui/admin/tests/e2e/tests/delete-resources.spec.js +++ b/ui/admin/tests/e2e/tests/delete-resources.spec.js @@ -5,6 +5,7 @@ /* eslint-disable no-undef */ const { test } = require('@playwright/test'); +const { execSync } = require('child_process'); const { authenticatedState, checkEnv } = require('../helpers/general'); const { @@ -29,13 +30,10 @@ const { createUsernamePasswordCredentialCli, deleteOrgCli, } = require('../helpers/boundary-cli'); - -const { - deleteResource, - removeAuthMethodAsPrimary, -} = require('../helpers/boundary-ui'); const { checkVaultCli } = require('../helpers/vault-cli'); -const { execSync } = require('child_process'); +const AuthMethodsPage = require('../pages/auth-methods'); +const BaseResourcePage = require('../pages/base-resource'); +const WorkersPage = require('../pages/workers'); const secretPolicyName = 'kv-policy'; const boundaryPolicyName = 'boundary-controller'; @@ -104,94 +102,98 @@ test('Verify resources can be deleted @ce @aws', async ({ page }) => { await createUsernamePasswordCredentialCli(staticCredentialStoreId); let tcpTargetId = await createTcpTarget(projectId); + const baseResourcePage = new BaseResourcePage(page); + // Delete TCP target await page.goto(`/scopes/${projectId}/targets/${tcpTargetId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete username-password credentials await page.goto( `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}/credentials/${usernamePasswordCredentialId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete static credential store await page.goto( `/scopes/${projectId}/credential-stores/${staticCredentialStoreId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete vault credential store await page.goto( `/scopes/${projectId}/credential-stores/${vaultCredentialStoreId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete static host set await page.goto( `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/host-sets/${staticHostSetId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete static host await page.goto( `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}/hosts/${staticHostId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete static host catalog await page.goto( `/scopes/${projectId}/host-catalogs/${staticHostCatalogId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete dynamic aws host catalog await page.goto( `/scopes/${projectId}/host-catalogs/${dynamicAwsHostCatalogId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete user await page.goto(`/scopes/${projectId}/users/${userId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete group await page.goto(`/scopes/${projectId}/groups/${groupId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete role under global scope await page.goto(`/scopes/global/roles/${globalScopeRoleId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete role under org scope await page.goto(`/scopes/${projectId}/roles/${orgScopeRoleId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete role under project scope await page.goto(`/scopes/${projectId}/roles/${projectScopeRoleId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete password account await page.goto( `/scopes/${orgId}/auth-methods/${authMethodId}/accounts/${passwordAccountId}`, ); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete auth method await page.goto(`/scopes/${orgId}/auth-methods/${authMethodId}`); - await removeAuthMethodAsPrimary(page); - await deleteResource(page); + const authMethodsPage = new AuthMethodsPage(page); + await authMethodsPage.removeAuthMethodAsPrimary(); + await baseResourcePage.deleteResource(page); // Delete worker await page.goto(`/scopes/global/workers/${workerId}`); - await deleteResource(page); + const workersPage = new WorkersPage(page); + await workersPage.deleteResource(page); // Delete project await page.goto(`/scopes/${projectId}`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); // Delete org await page.goto(`/scopes/${orgId}/edit`); - await deleteResource(page); + await baseResourcePage.deleteResource(page); orgDeleted = true; } finally { // Delete org in case the test failed before deleting the org using UI diff --git a/ui/admin/tests/e2e/tests/dynamic-host-catalog-aws.spec.js b/ui/admin/tests/e2e/tests/dynamic-host-catalog-aws.spec.js index 1ac5aef88..2c58504f7 100644 --- a/ui/admin/tests/e2e/tests/dynamic-host-catalog-aws.spec.js +++ b/ui/admin/tests/e2e/tests/dynamic-host-catalog-aws.spec.js @@ -5,21 +5,18 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); -import { execSync } from 'child_process'; const { nanoid } = require('nanoid'); +import { execSync } from 'child_process'; + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, deleteOrgCli, } = require('../helpers/boundary-cli'); -const { - addHostSourceToTarget, - createHostCatalog, - createHostSet, - createOrg, - createProject, - createTarget, -} = require('../helpers/boundary-ui'); +const HostCatalogsPage = require('../pages/host-catalogs'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const TargetsPage = require('../pages/targets'); test.use({ storageState: authenticatedState }); @@ -44,8 +41,10 @@ test.describe('AWS', async () => { }) => { let orgName; try { - orgName = await createOrg(page); - await createProject(page); + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); // Create host catalog const hostCatalogName = 'Host Catalog ' + nanoid(); @@ -165,18 +164,22 @@ test.describe('AWS', async () => { } // Create a target and add DHC host set as a host source - const targetName = await createTarget(page, process.env.E2E_TARGET_PORT); - await addHostSourceToTarget(page, hostSetName); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTarget( + process.env.E2E_TARGET_PORT, + ); + await targetsPage.addHostSourceToTarget(hostSetName); // Add another host source - await createHostCatalog(page); - const newHostSetName = await createHostSet(page); + const hostCatalogsPage = new HostCatalogsPage(page); + await hostCatalogsPage.createHostCatalog(); + const newHostSetName = await hostCatalogsPage.createHostSet(); await page .getByRole('navigation', { name: 'Resources' }) .getByRole('link', { name: 'Targets' }) .click(); await page.getByRole('link', { name: targetName }).click(); - await addHostSourceToTarget(page, newHostSetName); + await targetsPage.addHostSourceToTarget(newHostSetName); // Remove the host source from the target await page @@ -193,7 +196,7 @@ test.describe('AWS', async () => { await page.getByRole('button', { name: 'Dismiss' }).click(); // Add the host source back - await addHostSourceToTarget(page, newHostSetName); + await targetsPage.addHostSourceToTarget(newHostSetName); } finally { await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, diff --git a/ui/admin/tests/e2e/tests/global-settings.spec.js b/ui/admin/tests/e2e/tests/global-settings.spec.js index 3203d76bf..8229767f5 100644 --- a/ui/admin/tests/e2e/tests/global-settings.spec.js +++ b/ui/admin/tests/e2e/tests/global-settings.spec.js @@ -5,11 +5,10 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); + const { authenticatedState } = require('../helpers/general'); -const { - attachStoragePolicy, - createStoragePolicy, -} = require('../helpers/boundary-ui'); +const OrgsPage = require('../pages/orgs'); +const StoragePoliciesPage = require('../pages/storage-policies'); test.use({ storageState: authenticatedState }); @@ -17,8 +16,10 @@ test('Global Settings @ent @aws @docker', async ({ page }) => { await page.goto('/'); // Create a storage policy - const policyName = await createStoragePolicy(page); - await attachStoragePolicy(page, policyName); + const storagePoliciesPage = new StoragePoliciesPage(page); + const policyName = await storagePoliciesPage.createStoragePolicy(); + const orgsPage = new OrgsPage(page); + await orgsPage.attachStoragePolicy(policyName); // Detach storage policy await page.getByRole('button', { name: 'Manage', exact: true }).click(); diff --git a/ui/admin/tests/e2e/tests/group-role.spec.js b/ui/admin/tests/e2e/tests/group-role.spec.js index 0e0f0a066..556e61b34 100644 --- a/ui/admin/tests/e2e/tests/group-role.spec.js +++ b/ui/admin/tests/e2e/tests/group-role.spec.js @@ -6,20 +6,16 @@ /* eslint-disable no-undef */ const { test } = require('@playwright/test'); const { execSync } = require('child_process'); + const { authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, checkBoundaryCli, deleteOrgCli, } = require('../helpers/boundary-cli'); -const { - createOrg, - createGroup, - addMemberToGroup, - createRole, - addPrincipalToRole, - addGrantsToGroup, -} = require('../helpers/boundary-ui'); +const GroupsPage = require('../pages/groups'); +const OrgsPage = require('../pages/orgs'); +const RolesPage = require('../pages/roles'); test.use({ storageState: authenticatedState }); @@ -33,12 +29,15 @@ test('Verify a new role can be created and associated with a group @ce @ent @aws await page.goto('/'); let orgName; try { - orgName = await createOrg(page); - const groupName = await createGroup(page); - await addMemberToGroup(page, 'admin'); - await createRole(page); - await addPrincipalToRole(page, groupName); - await addGrantsToGroup(page, 'ids=*;type=*;actions=read,list'); + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const groupsPage = new GroupsPage(page); + const groupName = await groupsPage.createGroup(); + await groupsPage.addMemberToGroup('admin'); + const rolesPage = new RolesPage(page); + await rolesPage.createRole(); + await rolesPage.addPrincipalToRole(groupName); + await rolesPage.addGrantsToRole('ids=*;type=*;actions=read,list'); } finally { await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, diff --git a/ui/admin/tests/e2e/tests/pagination.spec.js b/ui/admin/tests/e2e/tests/pagination.spec.js index 83605f07d..33cf0b0e7 100644 --- a/ui/admin/tests/e2e/tests/pagination.spec.js +++ b/ui/admin/tests/e2e/tests/pagination.spec.js @@ -6,6 +6,7 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); import { nanoid } from 'nanoid'; + import { authenticatedState } from '../helpers/general'; test.use({ storageState: authenticatedState }); diff --git a/ui/admin/tests/e2e/tests/session-recording-aws-ent.spec.js b/ui/admin/tests/e2e/tests/session-recording-aws-ent.spec.js index 1604640b1..7c2255d5d 100644 --- a/ui/admin/tests/e2e/tests/session-recording-aws-ent.spec.js +++ b/ui/admin/tests/e2e/tests/session-recording-aws-ent.spec.js @@ -6,6 +6,7 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); const { execSync } = require('child_process'); + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, @@ -16,20 +17,13 @@ const { deleteStorageBucketCli, deletePolicyCli, } = require('../helpers/boundary-cli'); -const { - waitForSessionToBeVisible, - createStorageBucketAws, - enableSessionRecording, - createStoragePolicy, - attachStoragePolicy, - createOrg, - createProject, - createStaticCredentialStore, - createStaticCredentialKeyPair, - createSshTargetWithAddressEnt, - addInjectedCredentialsToTarget, - addEgressWorkerFilterToTarget, -} = require('../helpers/boundary-ui'); +const CredentialStoresPage = require('../pages/credential-stores'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const SessionsPage = require('../pages/sessions'); +const StorageBucketsPage = require('../pages/storage-buckets'); +const StoragePoliciesPage = require('../pages/storage-policies'); +const TargetsPage = require('../pages/targets'); test.use({ storageState: authenticatedState }); @@ -57,7 +51,8 @@ test('Session Recording Test (AWS) @ent @aws', async ({ page }) => { let connect; try { // Create org - const orgName = await createOrg(page); + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, process.env.E2E_PASSWORD_AUTH_METHOD_ID, @@ -69,7 +64,8 @@ test('Session Recording Test (AWS) @ent @aws', async ({ page }) => { orgId = org.id; // Create project - const projectName = await createProject(page); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); const projects = JSON.parse( execSync('boundary scopes list -format json -scope-id ' + org.id), ); @@ -78,8 +74,8 @@ test('Session Recording Test (AWS) @ent @aws', async ({ page }) => { // Create storage bucket await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - const storageBucketName = await createStorageBucketAws( - page, + const storageBucketsPage = new StorageBucketsPage(page); + const storageBucketName = await storageBucketsPage.createStorageBucketAws( orgName, process.env.E2E_AWS_BUCKET_NAME, process.env.E2E_AWS_REGION, @@ -99,28 +95,31 @@ test('Session Recording Test (AWS) @ent @aws', async ({ page }) => { await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); await page.getByRole('link', { name: orgName }).click(); await page.getByRole('link', { name: projectName }).click(); - const targetName = await createSshTargetWithAddressEnt( - page, + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); - await addEgressWorkerFilterToTarget( - page, + await targetsPage.addEgressWorkerFilterToTarget( `"${process.env.E2E_WORKER_TAG_EGRESS}" in "/tags/type"`, ); const targets = JSON.parse( execSync('boundary targets list -format json -scope-id ' + projectId), ); const target = targets.items.filter((obj) => obj.name == targetName)[0]; - await createStaticCredentialStore(page); - const credentialName = await createStaticCredentialKeyPair( - page, - process.env.E2E_SSH_USER, - process.env.E2E_SSH_KEY_PATH, + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + process.env.E2E_SSH_USER, + process.env.E2E_SSH_KEY_PATH, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await addInjectedCredentialsToTarget(page, targetName, credentialName); await page.getByRole('link', { name: targetName }).click(); - await enableSessionRecording(page, storageBucketName); + await targetsPage.enableSessionRecording(storageBucketName); // Create storage policy in org scope: keep session recordings forever await page.getByRole('link', { name: 'Orgs', exact: true }).click(); @@ -129,14 +128,16 @@ test('Session Recording Test (AWS) @ent @aws', async ({ page }) => { await expect( page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(orgName), ).toBeVisible(); - const policyName = await createStoragePolicy(page); - await attachStoragePolicy(page, policyName); + const storagePoliciesPage = new StoragePoliciesPage(page); + const policyName = await storagePoliciesPage.createStoragePolicy(); + await orgsPage.attachStoragePolicy(policyName); // Establish connection to target and cancel it connect = await connectSshToTarget(target.id); await page.getByRole('link', { name: 'Projects', exact: true }).click(); await page.getByRole('link', { name: projectName }).click(); - await waitForSessionToBeVisible(page, targetName); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); await page .getByRole('cell', { name: targetName }) .locator('..') diff --git a/ui/admin/tests/e2e/tests/session-recording-minio-ent.spec.js b/ui/admin/tests/e2e/tests/session-recording-minio-ent.spec.js index 5bc2d256b..95bd85563 100644 --- a/ui/admin/tests/e2e/tests/session-recording-minio-ent.spec.js +++ b/ui/admin/tests/e2e/tests/session-recording-minio-ent.spec.js @@ -6,6 +6,7 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); const { execSync } = require('child_process'); + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, @@ -16,20 +17,13 @@ const { deleteStorageBucketCli, deletePolicyCli, } = require('../helpers/boundary-cli'); -const { - waitForSessionToBeVisible, - createStorageBucketMinio, - enableSessionRecording, - createStoragePolicy, - attachStoragePolicy, - createOrg, - createProject, - createStaticCredentialStore, - createStaticCredentialKeyPair, - createSshTargetWithAddressEnt, - addInjectedCredentialsToTarget, - addEgressWorkerFilterToTarget, -} = require('../helpers/boundary-ui'); +const CredentialStoresPage = require('../pages/credential-stores'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const SessionsPage = require('../pages/sessions'); +const StorageBucketsPage = require('../pages/storage-buckets'); +const StoragePoliciesPage = require('../pages/storage-policies'); +const TargetsPage = require('../pages/targets'); test.use({ storageState: authenticatedState }); @@ -58,7 +52,8 @@ test('Session Recording Test (MinIO) @ent @docker', async ({ page }) => { let connect; try { // Create org - const orgName = await createOrg(page); + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, process.env.E2E_PASSWORD_AUTH_METHOD_ID, @@ -70,7 +65,8 @@ test('Session Recording Test (MinIO) @ent @docker', async ({ page }) => { orgId = org.id; // Create project - const projectName = await createProject(page); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); const projects = JSON.parse( execSync('boundary scopes list -format json -scope-id ' + org.id), ); @@ -79,8 +75,8 @@ test('Session Recording Test (MinIO) @ent @docker', async ({ page }) => { // Create storage bucket await page.getByRole('link', { name: 'Orgs', exact: true }).click(); - const storageBucketName = await createStorageBucketMinio( - page, + const storageBucketsPage = new StorageBucketsPage(page); + const storageBucketName = await storageBucketsPage.createStorageBucketMinio( orgName, process.env.E2E_BUCKET_ENDPOINT_URL, process.env.E2E_BUCKET_NAME, @@ -101,13 +97,12 @@ test('Session Recording Test (MinIO) @ent @docker', async ({ page }) => { await expect(page.getByRole('heading', { name: 'Orgs' })).toBeVisible(); await page.getByRole('link', { name: orgName }).click(); await page.getByRole('link', { name: projectName }).click(); - const targetName = await createSshTargetWithAddressEnt( - page, + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); - await addEgressWorkerFilterToTarget( - page, + await targetsPage.addEgressWorkerFilterToTarget( `"${process.env.E2E_WORKER_TAG_EGRESS}" in "/tags/type"`, ); @@ -115,15 +110,19 @@ test('Session Recording Test (MinIO) @ent @docker', async ({ page }) => { execSync('boundary targets list -format json -scope-id ' + projectId), ); const target = targets.items.filter((obj) => obj.name == targetName)[0]; - await createStaticCredentialStore(page); - const credentialName = await createStaticCredentialKeyPair( - page, - process.env.E2E_SSH_USER, - process.env.E2E_SSH_KEY_PATH, + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + process.env.E2E_SSH_USER, + process.env.E2E_SSH_KEY_PATH, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await addInjectedCredentialsToTarget(page, targetName, credentialName); await page.getByRole('link', { name: targetName }).click(); - await enableSessionRecording(page, storageBucketName); + await targetsPage.enableSessionRecording(storageBucketName); // Create storage policy in org scope: keep session recordings forever await page.getByRole('link', { name: 'Orgs', exact: true }).click(); @@ -132,14 +131,16 @@ test('Session Recording Test (MinIO) @ent @docker', async ({ page }) => { await expect( page.getByRole('navigation', { name: 'breadcrumbs' }).getByText(orgName), ).toBeVisible(); - const policyName = await createStoragePolicy(page); - await attachStoragePolicy(page, policyName); + const storagePoliciesPage = new StoragePoliciesPage(page); + const policyName = await storagePoliciesPage.createStoragePolicy(); + await orgsPage.attachStoragePolicy(policyName); // Establish connection to target and cancel it connect = await connectSshToTarget(target.id); await page.getByRole('link', { name: 'Projects', exact: true }).click(); await page.getByRole('link', { name: projectName }).click(); - await waitForSessionToBeVisible(page, targetName); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); await page .getByRole('cell', { name: targetName }) .locator('..') diff --git a/ui/admin/tests/e2e/tests/ssh-certificate-injection-vault-ent.spec.js b/ui/admin/tests/e2e/tests/ssh-certificate-injection-vault-ent.spec.js index 636484662..fe3507f27 100644 --- a/ui/admin/tests/e2e/tests/ssh-certificate-injection-vault-ent.spec.js +++ b/ui/admin/tests/e2e/tests/ssh-certificate-injection-vault-ent.spec.js @@ -6,6 +6,7 @@ /* eslint-disable no-undef */ const { test } = require('@playwright/test'); const { execSync } = require('child_process'); + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, @@ -14,15 +15,11 @@ const { deleteOrgCli, } = require('../helpers/boundary-cli'); const { checkVaultCli } = require('../helpers/vault-cli'); -const { - createOrg, - createProject, - createSshTargetWithAddressEnt, - createVaultCredentialStore, - createVaultSshCertificateCredentialLibrary, - addInjectedCredentialsToTarget, - waitForSessionToBeVisible, -} = require('../helpers/boundary-ui'); +const CredentialStoresPage = require('../pages/credential-stores'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const SessionsPage = require('../pages/sessions'); +const TargetsPage = require('../pages/targets'); const sshPolicyName = 'ssh-policy'; const boundaryPolicyName = 'boundary-controller'; @@ -96,7 +93,8 @@ test('SSH Certificate Injection @ent @docker', async ({ page }) => { const clientToken = vaultToken.auth.client_token; // Create org - const orgName = await createOrg(page); + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, process.env.E2E_PASSWORD_AUTH_METHOD_ID, @@ -107,15 +105,16 @@ test('SSH Certificate Injection @ent @docker', async ({ page }) => { org = orgs.items.filter((obj) => obj.name == orgName)[0]; // Create project - const projectName = await createProject(page); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); const projects = JSON.parse( execSync('boundary scopes list -format json -scope-id ' + org.id), ); const project = projects.items.filter((obj) => obj.name == projectName)[0]; // Create target - const targetName = await createSshTargetWithAddressEnt( - page, + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); @@ -125,26 +124,25 @@ test('SSH Certificate Injection @ent @docker', async ({ page }) => { const target = targets.items.filter((obj) => obj.name == targetName)[0]; // Create credentials - await createVaultCredentialStore( - page, + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createVaultCredentialStore( process.env.E2E_VAULT_ADDR, clientToken, ); const credentialLibraryName = - await createVaultSshCertificateCredentialLibrary( - page, + await credentialStoresPage.createVaultSshCertificateCredentialLibrary( `${secretsPath}/issue/${secretName}`, process.env.E2E_SSH_USER, ); - await addInjectedCredentialsToTarget( - page, + await targetsPage.addInjectedCredentialsToTarget( targetName, credentialLibraryName, ); // Connect to target connect = await connectSshToTarget(target.id); - await waitForSessionToBeVisible(page, targetName); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); await page .getByRole('cell', { name: targetName }) .locator('..') diff --git a/ui/admin/tests/e2e/tests/ssh-credential-injection-vault-ent.spec.js b/ui/admin/tests/e2e/tests/ssh-credential-injection-vault-ent.spec.js index 627294a88..5739f4b64 100644 --- a/ui/admin/tests/e2e/tests/ssh-credential-injection-vault-ent.spec.js +++ b/ui/admin/tests/e2e/tests/ssh-credential-injection-vault-ent.spec.js @@ -6,6 +6,7 @@ /* eslint-disable no-undef */ const { test } = require('@playwright/test'); const { execSync } = require('child_process'); + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, @@ -14,15 +15,11 @@ const { deleteOrgCli, } = require('../helpers/boundary-cli'); const { checkVaultCli } = require('../helpers/vault-cli'); -const { - createOrg, - createProject, - createSshTargetWithAddressEnt, - createVaultCredentialStore, - createVaultGenericCredentialLibrary, - addInjectedCredentialsToTarget, - waitForSessionToBeVisible, -} = require('../helpers/boundary-ui'); +const CredentialStoresPage = require('../pages/credential-stores'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const SessionsPage = require('../pages/sessions'); +const TargetsPage = require('../pages/targets'); const secretsPath = 'e2e_secrets'; const secretName = 'cred'; @@ -87,7 +84,8 @@ test('SSH Credential Injection (Vault User & Key Pair) @ent @docker', async ({ const clientToken = vaultToken.auth.client_token; // Create org - const orgName = await createOrg(page); + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, process.env.E2E_PASSWORD_AUTH_METHOD_ID, @@ -98,15 +96,16 @@ test('SSH Credential Injection (Vault User & Key Pair) @ent @docker', async ({ org = orgs.items.filter((obj) => obj.name == orgName)[0]; // Create project - const projectName = await createProject(page); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); const projects = JSON.parse( execSync('boundary scopes list -format json -scope-id ' + org.id), ); const project = projects.items.filter((obj) => obj.name == projectName)[0]; // Create target - const targetName = await createSshTargetWithAddressEnt( - page, + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); @@ -116,25 +115,25 @@ test('SSH Credential Injection (Vault User & Key Pair) @ent @docker', async ({ const target = targets.items.filter((obj) => obj.name == targetName)[0]; // Create credentials - await createVaultCredentialStore( - page, + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createVaultCredentialStore( process.env.E2E_VAULT_ADDR, clientToken, ); - const credentialLibraryName = await createVaultGenericCredentialLibrary( - page, - `${secretsPath}/data/${secretName}`, - 'SSH Private Key', - ); - await addInjectedCredentialsToTarget( - page, + const credentialLibraryName = + await credentialStoresPage.createVaultGenericCredentialLibrary( + `${secretsPath}/data/${secretName}`, + 'SSH Private Key', + ); + await targetsPage.addInjectedCredentialsToTarget( targetName, credentialLibraryName, ); // Connect to target connect = await connectSshToTarget(target.id); - await waitForSessionToBeVisible(page, targetName); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); await page .getByRole('cell', { name: targetName }) .locator('..') diff --git a/ui/admin/tests/e2e/tests/target-ent.spec.js b/ui/admin/tests/e2e/tests/target-ent.spec.js index 873ff7948..e923133af 100644 --- a/ui/admin/tests/e2e/tests/target-ent.spec.js +++ b/ui/admin/tests/e2e/tests/target-ent.spec.js @@ -6,6 +6,7 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); const { execSync } = require('child_process'); + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, @@ -14,21 +15,12 @@ const { connectSshToTarget, deleteOrgCli, } = require('../helpers/boundary-cli'); -const { - addHostSourceToTarget, - createHostCatalog, - createHostInHostSet, - createHostSet, - createOrg, - createProject, - createStaticCredentialStore, - createStaticCredentialKeyPair, - addInjectedCredentialsToTarget, - createTcpTargetWithAddressEnt, - createSshTargetEnt, - createSshTargetWithAddressEnt, - waitForSessionToBeVisible, -} = require('../helpers/boundary-ui'); +const CredentialStoresPage = require('../pages/credential-stores'); +const HostCatalogsPage = require('../pages/host-catalogs'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const SessionsPage = require('../pages/sessions'); +const TargetsPage = require('../pages/targets'); test.use({ storageState: authenticatedState }); @@ -50,7 +42,8 @@ test('Verify session created for TCP target @ent @aws @docker', async ({ let org; let connect; try { - const orgName = await createOrg(page); + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, process.env.E2E_PASSWORD_AUTH_METHOD_ID, @@ -60,14 +53,15 @@ test('Verify session created for TCP target @ent @aws @docker', async ({ const orgs = JSON.parse(execSync('boundary scopes list -format json')); org = orgs.items.filter((obj) => obj.name == orgName)[0]; - const projectName = await createProject(page); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); const projects = JSON.parse( execSync('boundary scopes list -format json -scope-id ' + org.id), ); const project = projects.items.filter((obj) => obj.name == projectName)[0]; - const targetName = await createTcpTargetWithAddressEnt( - page, + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTcpTargetWithAddressEnt( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); @@ -81,7 +75,8 @@ test('Verify session created for TCP target @ent @aws @docker', async ({ process.env.E2E_SSH_USER, process.env.E2E_SSH_KEY_PATH, ); - await waitForSessionToBeVisible(page, targetName); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); await page .getByRole('cell', { name: targetName }) .locator('..') @@ -105,7 +100,8 @@ test('Verify session created for SSH target @ent @aws @docker', async ({ let org; let connect; try { - const orgName = await createOrg(page); + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, process.env.E2E_PASSWORD_AUTH_METHOD_ID, @@ -115,14 +111,15 @@ test('Verify session created for SSH target @ent @aws @docker', async ({ const orgs = JSON.parse(execSync('boundary scopes list -format json')); org = orgs.items.filter((obj) => obj.name == orgName)[0]; - const projectName = await createProject(page); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); const projects = JSON.parse( execSync('boundary scopes list -format json -scope-id ' + org.id), ); const project = projects.items.filter((obj) => obj.name == projectName)[0]; - const targetName = await createSshTargetWithAddressEnt( - page, + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetWithAddressEnt( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); @@ -131,16 +128,21 @@ test('Verify session created for SSH target @ent @aws @docker', async ({ ); const target = targets.items.filter((obj) => obj.name == targetName)[0]; - await createStaticCredentialStore(page); - const credentialName = await createStaticCredentialKeyPair( - page, - process.env.E2E_SSH_USER, - process.env.E2E_SSH_KEY_PATH, + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + process.env.E2E_SSH_USER, + process.env.E2E_SSH_KEY_PATH, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await addInjectedCredentialsToTarget(page, targetName, credentialName); connect = await connectSshToTarget(target.id); - await waitForSessionToBeVisible(page, targetName); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); await page .getByRole('cell', { name: targetName }) .locator('..') @@ -162,13 +164,16 @@ test('SSH target with host sources @ent @aws @docker', async ({ page }) => { let org; let connect; try { - const orgName = await createOrg(page); - const projectName = await createProject(page); + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); // Create host set - const hostCatalogName = await createHostCatalog(page); - const hostSetName = await createHostSet(page); - await createHostInHostSet(page, process.env.E2E_TARGET_ADDRESS); + const hostCatalogsPage = new HostCatalogsPage(page); + const hostCatalogName = await hostCatalogsPage.createHostCatalog(); + const hostSetName = await hostCatalogsPage.createHostSet(); + await hostCatalogsPage.createHostInHostSet(process.env.E2E_TARGET_ADDRESS); // Create another host set await page @@ -176,17 +181,17 @@ test('SSH target with host sources @ent @aws @docker', async ({ page }) => { .getByRole('link', { name: 'Host Catalogs' }) .click(); await page.getByRole('link', { name: hostCatalogName }).click(); - const hostSetName2 = await createHostSet(page); + const hostSetName2 = await hostCatalogsPage.createHostSet(); // Create target - const targetName = await createSshTargetEnt( - page, + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createSshTargetEnt( process.env.E2E_TARGET_PORT, ); - await addHostSourceToTarget(page, hostSetName); + await targetsPage.addHostSourceToTarget(hostSetName); // Add/Remove another host source - await addHostSourceToTarget(page, hostSetName2); + await targetsPage.addHostSourceToTarget(hostSetName2); await page .getByRole('link', { name: hostSetName2 }) .locator('..') @@ -201,13 +206,17 @@ test('SSH target with host sources @ent @aws @docker', async ({ page }) => { await page.getByRole('button', { name: 'Dismiss' }).click(); // Create credentials and attach to target - await createStaticCredentialStore(page); - const credentialName = await createStaticCredentialKeyPair( - page, - process.env.E2E_SSH_USER, - process.env.E2E_SSH_KEY_PATH, + const credentialStoresPage = new CredentialStoresPage(page); + await credentialStoresPage.createStaticCredentialStore(); + const credentialName = + await credentialStoresPage.createStaticCredentialKeyPair( + process.env.E2E_SSH_USER, + process.env.E2E_SSH_KEY_PATH, + ); + await targetsPage.addInjectedCredentialsToTarget( + targetName, + credentialName, ); - await addInjectedCredentialsToTarget(page, targetName, credentialName); // Connect to target await authenticateBoundaryCli( @@ -227,7 +236,8 @@ test('SSH target with host sources @ent @aws @docker', async ({ page }) => { ); const target = targets.items.filter((obj) => obj.name == targetName)[0]; connect = await connectSshToTarget(target.id); - await waitForSessionToBeVisible(page, targetName); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); await page .getByRole('cell', { name: targetName }) .locator('..') diff --git a/ui/admin/tests/e2e/tests/target.spec.js b/ui/admin/tests/e2e/tests/target.spec.js index b8de752d9..97f988219 100644 --- a/ui/admin/tests/e2e/tests/target.spec.js +++ b/ui/admin/tests/e2e/tests/target.spec.js @@ -6,6 +6,7 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); const { execSync } = require('child_process'); + const { checkEnv, authenticatedState } = require('../helpers/general'); const { authenticateBoundaryCli, @@ -13,18 +14,11 @@ const { connectToTarget, deleteOrgCli, } = require('../helpers/boundary-cli'); -const { - createOrg, - createProject, - createHostCatalog, - createHostSet, - createHostInHostSet, - createTarget, - createTargetWithAddress, - waitForSessionToBeVisible, - addHostSourceToTarget, - addEgressWorkerFilterToTarget, -} = require('../helpers/boundary-ui'); +const HostCatalogsPage = require('../pages/host-catalogs'); +const OrgsPage = require('../pages/orgs'); +const ProjectsPage = require('../pages/projects'); +const SessionsPage = require('../pages/sessions'); +const TargetsPage = require('../pages/targets'); test.use({ storageState: authenticatedState }); @@ -46,13 +40,16 @@ test('Verify session created to target with host, then cancel the session @ce @a let org; let connect; try { - const orgName = await createOrg(page); - const projectName = await createProject(page); + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); // Create host set - const hostCatalogName = await createHostCatalog(page); - const hostSetName = await createHostSet(page); - await createHostInHostSet(page, process.env.E2E_TARGET_ADDRESS); + const hostCatalogsPage = new HostCatalogsPage(page); + const hostCatalogName = await hostCatalogsPage.createHostCatalog(); + const hostSetName = await hostCatalogsPage.createHostSet(); + await hostCatalogsPage.createHostInHostSet(process.env.E2E_TARGET_ADDRESS); // Create another host set await page @@ -60,14 +57,17 @@ test('Verify session created to target with host, then cancel the session @ce @a .getByRole('link', { name: 'Host Catalogs' }) .click(); await page.getByRole('link', { name: hostCatalogName }).click(); - const hostSetName2 = await createHostSet(page); + const hostSetName2 = await hostCatalogsPage.createHostSet(); // Create target - const targetName = await createTarget(page, process.env.E2E_TARGET_PORT); - await addHostSourceToTarget(page, hostSetName); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTarget( + process.env.E2E_TARGET_PORT, + ); + await targetsPage.addHostSourceToTarget(hostSetName); // Add/Remove another host source - await addHostSourceToTarget(page, hostSetName2); + await targetsPage.addHostSourceToTarget(hostSetName2); await page .getByRole('link', { name: hostSetName2 }) .locator('..') @@ -104,7 +104,8 @@ test('Verify session created to target with host, then cancel the session @ce @a process.env.E2E_SSH_USER, process.env.E2E_SSH_KEY_PATH, ); - await waitForSessionToBeVisible(page, targetName); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); await page .getByRole('cell', { name: targetName }) .locator('..') @@ -128,10 +129,12 @@ test('Verify session created to target with address, then cancel the session @ce let org; let connect; try { - const orgName = await createOrg(page); - const projectName = await createProject(page); - const targetName = await createTargetWithAddress( - page, + const orgsPage = new OrgsPage(page); + const orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + const projectName = await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + const targetName = await targetsPage.createTargetWithAddress( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); @@ -158,7 +161,8 @@ test('Verify session created to target with address, then cancel the session @ce process.env.E2E_SSH_USER, process.env.E2E_SSH_KEY_PATH, ); - await waitForSessionToBeVisible(page, targetName); + const sessionsPage = new SessionsPage(page); + await sessionsPage.waitForSessionToBeVisible(targetName); await page .getByRole('cell', { name: targetName }) .locator('..') @@ -179,10 +183,12 @@ test('Verify TCP target is updated @ce @aws @docker', async ({ page }) => { await page.goto('/'); let orgName; try { - orgName = await createOrg(page); - await createProject(page); - await createTargetWithAddress( - page, + const orgsPage = new OrgsPage(page); + orgName = await orgsPage.createOrg(); + const projectsPage = new ProjectsPage(page); + await projectsPage.createProject(); + const targetsPage = new TargetsPage(page); + await targetsPage.createTargetWithAddress( process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, ); @@ -203,7 +209,7 @@ test('Verify TCP target is updated @ce @aws @docker', async ({ page }) => { ).toBeVisible(); await page.getByRole('button', { name: 'Dismiss' }).click(); - await addEgressWorkerFilterToTarget(page, '"dev" in "/tags/type"'); + await targetsPage.addEgressWorkerFilterToTarget('"dev" in "/tags/type"'); } finally { await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, diff --git a/ui/admin/tests/e2e/tests/worker-ent.spec.js b/ui/admin/tests/e2e/tests/worker-ent.spec.js index a3ed8d131..0b14f0046 100644 --- a/ui/admin/tests/e2e/tests/worker-ent.spec.js +++ b/ui/admin/tests/e2e/tests/worker-ent.spec.js @@ -6,6 +6,7 @@ /* eslint-disable no-undef */ const { expect } = require('@playwright/test'); const { test } = require('@playwright/test'); + const { authenticatedState } = require('../helpers/general'); test.use({ storageState: authenticatedState }); diff --git a/ui/admin/tests/e2e/tests/worker.spec.js b/ui/admin/tests/e2e/tests/worker.spec.js index 76e2237fc..4610e9901 100644 --- a/ui/admin/tests/e2e/tests/worker.spec.js +++ b/ui/admin/tests/e2e/tests/worker.spec.js @@ -6,6 +6,7 @@ /* eslint-disable no-undef */ const { expect } = require('@playwright/test'); const { test } = require('@playwright/test'); + const { authenticatedState } = require('../helpers/general'); test.use({ storageState: authenticatedState });