From 40c1b3c6b53c9018c6667e8f64f45c039f2a0914 Mon Sep 17 00:00:00 2001 From: Michael Li Date: Mon, 17 Jun 2024 10:01:13 -0400 Subject: [PATCH] test(e2e): Add check for multiple host sources (#2331) * test(e2e): Add check for multiple host sets * CR: TCP targets don't have injected credentials * test(e2e): Add "exact: true" for OK button locators Sometimes, the OK button will get interfered by resource ids that contain an OK. --- ui/admin/tests/e2e/helpers/boundary-ui.js | 47 +++++++++- .../tests/dynamic-host-catalog-aws.spec.js | 2 +- .../tests/session-recording-aws-ent.spec.js | 2 +- .../tests/session-recording-minio-ent.spec.js | 2 +- ui/admin/tests/e2e/tests/target-ent.spec.js | 94 ++++++++++++++++++- ui/admin/tests/e2e/tests/target.spec.js | 30 +++++- 6 files changed, 171 insertions(+), 6 deletions(-) diff --git a/ui/admin/tests/e2e/helpers/boundary-ui.js b/ui/admin/tests/e2e/helpers/boundary-ui.js index 6b4aeb07b..d908f89b7 100644 --- a/ui/admin/tests/e2e/helpers/boundary-ui.js +++ b/ui/admin/tests/e2e/helpers/boundary-ui.js @@ -113,7 +113,22 @@ exports.createHostCatalog = async (page) => { exports.createHostSet = async (page) => { const hostSetName = 'Host Set ' + nanoid(); await page.getByRole('link', { name: 'Host Sets' }).click(); - await page.getByRole('link', { name: 'New', exact: true }).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(); @@ -432,6 +447,36 @@ exports.createTcpTargetWithAddressEnt = async (page, address, port) => { 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. 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 465fbd030..4ecf30798 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 @@ -187,7 +187,7 @@ test.describe('AWS', async () => { .getByRole('button', { name: 'Manage' }) .click(); await page.getByRole('button', { name: 'Remove' }).click(); - await page.getByRole('button', { name: 'OK' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); await expect( page.getByRole('alert').getByText('Success', { exact: true }), ).toBeVisible(); 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 8b0010e6c..de163ac8d 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 @@ -223,7 +223,7 @@ test('Session Recording Test (AWS) @ent @aws', async ({ page }) => { // Delete session recording await page.getByText('Manage').click(); await page.getByRole('button', { name: 'Delete recording' }).click(); - await page.getByRole('button', { name: 'OK' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); await expect( page.getByRole('alert').getByText('Success', { exact: true }), ).toBeVisible(); 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 1e226130f..00fa9f737 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 @@ -225,7 +225,7 @@ test('Session Recording Test (MinIO) @ent @docker', async ({ page }) => { // Delete session recording await page.getByText('Manage').click(); await page.getByRole('button', { name: 'Delete recording' }).click(); - await page.getByRole('button', { name: 'OK' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); await expect( page.getByRole('alert').getByText('Success', { exact: true }), ).toBeVisible(); diff --git a/ui/admin/tests/e2e/tests/target-ent.spec.js b/ui/admin/tests/e2e/tests/target-ent.spec.js index 229ebf4da..a950b75b8 100644 --- a/ui/admin/tests/e2e/tests/target-ent.spec.js +++ b/ui/admin/tests/e2e/tests/target-ent.spec.js @@ -4,7 +4,7 @@ */ /* eslint-disable no-undef */ -const { test } = require('@playwright/test'); +const { test, expect } = require('@playwright/test'); const { execSync } = require('child_process'); const { checkEnv, authenticatedState } = require('../helpers/general'); const { @@ -15,12 +15,17 @@ const { deleteOrgCli, } = require('../helpers/boundary-cli'); const { + addHostSourceToTarget, + createHostCatalog, + createHostInHostSet, + createHostSet, createOrg, createProject, createStaticCredentialStore, createStaticCredentialKeyPair, addInjectedCredentialsToTarget, createTcpTargetWithAddressEnt, + createSshTargetEnt, createSshTargetWithAddressEnt, waitForSessionToBeVisible, } = require('../helpers/boundary-ui'); @@ -152,3 +157,90 @@ test('Verify session created for SSH target @ent @aws @docker', async ({ } } }); + +test('SSH target with host sources @ent @aws @docker', async ({ page }) => { + await page.goto('/'); + let org; + let connect; + try { + const orgName = await createOrg(page); + const projectName = await createProject(page); + + // Create host set + const hostCatalogName = await createHostCatalog(page); + const hostSetName = await createHostSet(page); + await createHostInHostSet(page, process.env.E2E_TARGET_ADDRESS); + + // Create another host set + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Host Catalogs' }) + .click(); + await page.getByRole('link', { name: hostCatalogName }).click(); + const hostSetName2 = await createHostSet(page); + + // Create target + const targetName = await createSshTargetEnt( + page, + process.env.E2E_TARGET_PORT, + ); + await addHostSourceToTarget(page, hostSetName); + + // Add/Remove another host source + await addHostSourceToTarget(page, hostSetName2); + await page + .getByRole('link', { name: hostSetName2 }) + .locator('..') + .locator('..') + .getByRole('button', { name: 'Manage' }) + .click(); + await page.getByRole('button', { name: 'Remove' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + 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, + ); + await addInjectedCredentialsToTarget(page, targetName, credentialName); + + // Connect to target + 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, + ); + const orgs = JSON.parse(execSync('boundary scopes list -format json')); + org = orgs.items.filter((obj) => obj.name == orgName)[0]; + 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 targets = JSON.parse( + execSync('boundary targets list -format json -scope-id ' + project.id), + ); + const target = targets.items.filter((obj) => obj.name == targetName)[0]; + connect = await connectSshToTarget(target.id); + await waitForSessionToBeVisible(page, targetName); + await page + .getByRole('cell', { name: targetName }) + .locator('..') + .getByRole('button', { name: 'Cancel' }) + .click(); + } finally { + if (org) { + await deleteOrgCli(org.id); + } + // End `boundary connect` process + if (connect) { + connect.kill('SIGTERM'); + } + } +}); diff --git a/ui/admin/tests/e2e/tests/target.spec.js b/ui/admin/tests/e2e/tests/target.spec.js index fc2e5198c..bcb4c5264 100644 --- a/ui/admin/tests/e2e/tests/target.spec.js +++ b/ui/admin/tests/e2e/tests/target.spec.js @@ -47,12 +47,40 @@ test('Verify session created to target with host, then cancel the session @ce @a try { const orgName = await createOrg(page); const projectName = await createProject(page); - await createHostCatalog(page); + + // Create host set + const hostCatalogName = await createHostCatalog(page); const hostSetName = await createHostSet(page); await createHostInHostSet(page, process.env.E2E_TARGET_ADDRESS); + + // Create another host set + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Host Catalogs' }) + .click(); + await page.getByRole('link', { name: hostCatalogName }).click(); + const hostSetName2 = await createHostSet(page); + + // Create target const targetName = await createTarget(page, process.env.E2E_TARGET_PORT); await addHostSourceToTarget(page, hostSetName); + // Add/Remove another host source + await addHostSourceToTarget(page, hostSetName2); + await page + .getByRole('link', { name: hostSetName2 }) + .locator('..') + .locator('..') + .getByRole('button', { name: 'Manage' }) + .click(); + await page.getByRole('button', { name: 'Remove' }).click(); + await page.getByRole('button', { name: 'OK', exact: true }).click(); + await expect( + page.getByRole('alert').getByText('Success', { exact: true }), + ).toBeVisible(); + await page.getByRole('button', { name: 'Dismiss' }).click(); + + // Connect to target await authenticateBoundaryCli( process.env.BOUNDARY_ADDR, process.env.E2E_PASSWORD_AUTH_METHOD_ID,