From e73bded6aedd517673a3548abede74e015524cb7 Mon Sep 17 00:00:00 2001 From: Michael Li Date: Wed, 24 Jul 2024 14:55:51 -0400 Subject: [PATCH] test(e2e): Update tests due to 0.17.0 changes (#2401) * test(e2e): Only clean up org if org hasn't been deleted yet * test(e2e): Use new worker filter tab * test(e2e): Update selector for updated Manage button Some pages now use a Manage button instead of a Manage link. The "Title" selector was not being picked up on the Manage button. * test(e2e): Fix flakiness due to race condition The test should navigate to the home page after creating all of the resources using the API * test(e2e): Add cleanup to test --- ui/admin/tests/e2e/helpers/boundary-ui.js | 73 ++--- .../e2e/tests/delete-resources-ent.spec.js | 4 +- .../tests/e2e/tests/delete-resources.spec.js | 4 +- .../tests/dynamic-host-catalog-aws.spec.js | 297 ++++++++++-------- ui/admin/tests/e2e/tests/pagination.spec.js | 2 +- .../tests/session-recording-aws-ent.spec.js | 8 +- .../tests/session-recording-minio-ent.spec.js | 9 +- ...ssh-credential-injection-vault-ent.spec.js | 1 - ui/admin/tests/e2e/tests/target-ent.spec.js | 1 - ui/admin/tests/e2e/tests/target.spec.js | 8 +- 10 files changed, 218 insertions(+), 189 deletions(-) diff --git a/ui/admin/tests/e2e/helpers/boundary-ui.js b/ui/admin/tests/e2e/helpers/boundary-ui.js index ad107059c..c0144ea12 100644 --- a/ui/admin/tests/e2e/helpers/boundary-ui.js +++ b/ui/admin/tests/e2e/helpers/boundary-ui.js @@ -683,46 +683,6 @@ exports.createSshTargetWithAddressAndAlias = async ( 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 - * @param {string} workerFilterEgress Egress worker filter - * @returns Name of the target - */ -exports.createSshTargetWithAddressAndWorkerFilterEnt = async ( - page, - address, - port, - workerFilterEgress, -) => { - 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.getByLabel('Egress worker filter').click(); - await page.getByRole('textbox', { name: 'Filter' }).fill(workerFilterEgress); - 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 @@ -774,7 +734,7 @@ exports.createAliasForTarget = async (page, alias, targetId) => { * @param {Page} page Playwright page object */ exports.deleteResource = async (page) => { - await page.getByTitle('Manage').click(); + await page.getByText('Manage').click(); await page.getByRole('button', { name: /^(Delete|Remove Worker)/ }).click(); await page.getByText('OK', { exact: true }).click(); await expect( @@ -837,6 +797,37 @@ exports.addHostSourceToTarget = async (page, hostSourceName) => { 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. 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 3db27f110..7353202d3 100644 --- a/ui/admin/tests/e2e/tests/delete-resources-ent.spec.js +++ b/ui/admin/tests/e2e/tests/delete-resources-ent.spec.js @@ -74,6 +74,7 @@ test('Verify resources can be deleted (enterprise) @ent @aws', async ({ page, }) => { let orgId; + let orgDeleted = false; try { orgId = await createOrgCli(); let projectId = await createProjectCli(orgId); @@ -170,8 +171,9 @@ test('Verify resources can be deleted (enterprise) @ent @aws', async ({ await deleteResource(page); await page.goto(`/scopes/${orgId}/edit`); await deleteResource(page); + orgDeleted = true; } finally { - if (orgId) { + if (orgId && orgDeleted == false) { await deleteOrgCli(orgId); } } diff --git a/ui/admin/tests/e2e/tests/delete-resources.spec.js b/ui/admin/tests/e2e/tests/delete-resources.spec.js index 6f1ce72c7..f04273ff3 100644 --- a/ui/admin/tests/e2e/tests/delete-resources.spec.js +++ b/ui/admin/tests/e2e/tests/delete-resources.spec.js @@ -71,6 +71,7 @@ test.beforeEach(async () => { test('Verify resources can be deleted @ce @aws', async ({ page }) => { let orgId; + let orgDeleted = false; try { // Create boundary resources using CLI orgId = await createOrgCli(); @@ -191,9 +192,10 @@ test('Verify resources can be deleted @ce @aws', async ({ page }) => { // Delete org await page.goto(`/scopes/${orgId}/edit`); await deleteResource(page); + orgDeleted = true; } finally { // Delete org in case the test failed before deleting the org using UI - if (orgId) { + if (orgId && orgDeleted === false) { await deleteOrgCli(orgId); } } 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 c5979e1e2..1ac5aef88 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,8 +5,13 @@ /* eslint-disable no-undef */ const { test, expect } = require('@playwright/test'); +import { execSync } from 'child_process'; const { nanoid } = require('nanoid'); const { checkEnv, authenticatedState } = require('../helpers/general'); +const { + authenticateBoundaryCli, + deleteOrgCli, +} = require('../helpers/boundary-cli'); const { addHostSourceToTarget, createHostCatalog, @@ -37,151 +42,173 @@ test.describe('AWS', async () => { test('Create an AWS Dynamic Host Catalog and set up Host Sets @ce @ent @aws', async ({ page, }) => { - await createOrg(page); - await createProject(page); + let orgName; + try { + orgName = await createOrg(page); + await createProject(page); - // Create host catalog - const hostCatalogName = 'Host Catalog ' + nanoid(); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Host Catalogs' }) - .click(); - await page.getByRole('link', { name: 'New', 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('Dynamic') - .click(); - await page - .getByRole('group', { name: 'Provider' }) - .getByLabel('AWS') - .click(); - await page.getByLabel('AWS Region').fill(process.env.E2E_AWS_REGION); - await page - .getByLabel('Access Key ID') - .fill(process.env.E2E_AWS_ACCESS_KEY_ID); - await page - .getByLabel('Secret Access Key') - .fill(process.env.E2E_AWS_SECRET_ACCESS_KEY); - await page.getByLabel('Disable credential rotation').click({ force: true }); - 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(); - - // Create first host set - const hostSetName = 'Host Set ' + nanoid(); - await page.getByRole('link', { name: 'Host Sets' }).click(); - await page.getByRole('link', { name: 'New', exact: true }).click(); - await page.getByLabel('Name (Optional)').fill(hostSetName); - await page.getByLabel('Description').fill('This is an automated test'); - await page - .getByRole('group', { name: 'Filter' }) - .getByRole('textbox') - .fill(process.env.E2E_AWS_HOST_SET_FILTER); - await page - .getByRole('group', { name: 'Filter' }) - .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(hostSetName), - ).toBeVisible(); - - await page.getByRole('link', { name: 'Hosts' }).click(); - await expect( - page.getByRole('navigation', { name: 'breadcrumbs' }).getByText('Hosts'), - ).toBeVisible(); - - // Check number of hosts in host set - let i = 0; - let rowCount = 0; - let expectedHosts = JSON.parse(process.env.E2E_AWS_HOST_SET_IPS); - do { - i = i + 1; - // Getting the number of rows in the second rowgroup (the first rowgroup is the header row) - rowCount = await page - .getByRole('table') - .getByRole('rowgroup') - .nth(1) - .getByRole('row') - .count(); - await page.reload(); + // Create host catalog + const hostCatalogName = 'Host Catalog ' + nanoid(); await page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostSetName) - .waitFor(); - } while (i < 5); - - expect(rowCount).toBe(expectedHosts.length); - - // Navigate to each host in the host set - for (let i = 0; i < expectedHosts.length; i++) { - const host = await page - .getByRole('table') - .getByRole('rowgroup') - .nth(1) - .getByRole('row') - .nth(i) - .getByRole('link'); - - let hostName = await host.innerText(); - await host.click(); + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Host Catalogs' }) + .click(); + await page.getByRole('link', { name: 'New', 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('Dynamic') + .click(); + await page + .getByRole('group', { name: 'Provider' }) + .getByLabel('AWS') + .click(); + await page.getByLabel('AWS Region').fill(process.env.E2E_AWS_REGION); + await page + .getByLabel('Access Key ID') + .fill(process.env.E2E_AWS_ACCESS_KEY_ID); + await page + .getByLabel('Secret Access Key') + .fill(process.env.E2E_AWS_SECRET_ACCESS_KEY); + await page + .getByLabel('Disable credential rotation') + .click({ force: true }); + 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(hostName), + .getByText(hostCatalogName), ).toBeVisible(); + // Create first host set + const hostSetName = 'Host Set ' + nanoid(); + await page.getByRole('link', { name: 'Host Sets' }).click(); + await page.getByRole('link', { name: 'New', exact: true }).click(); + await page.getByLabel('Name (Optional)').fill(hostSetName); + await page.getByLabel('Description').fill('This is an automated test'); await page - .getByRole('navigation', { name: 'breadcrumbs' }) - .getByText(hostSetName) + .getByRole('group', { name: 'Filter' }) + .getByRole('textbox') + .fill(process.env.E2E_AWS_HOST_SET_FILTER); + await page + .getByRole('group', { name: 'Filter' }) + .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(hostSetName), + ).toBeVisible(); + await page.getByRole('link', { name: 'Hosts' }).click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText('Hosts'), + ).toBeVisible(); + + // Check number of hosts in host set + let i = 0; + let rowCount = 0; + let expectedHosts = JSON.parse(process.env.E2E_AWS_HOST_SET_IPS); + do { + i = i + 1; + // Getting the number of rows in the second rowgroup (the first rowgroup is the header row) + rowCount = await page + .getByRole('table') + .getByRole('rowgroup') + .nth(1) + .getByRole('row') + .count(); + await page.reload(); + await page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostSetName) + .waitFor(); + } while (i < 5); + + expect(rowCount).toBe(expectedHosts.length); + + // Navigate to each host in the host set + for (let i = 0; i < expectedHosts.length; i++) { + const host = await page + .getByRole('table') + .getByRole('rowgroup') + .nth(1) + .getByRole('row') + .nth(i) + .getByRole('link'); + + let hostName = await host.innerText(); + await host.click(); + await expect( + page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostName), + ).toBeVisible(); + + await page + .getByRole('navigation', { name: 'breadcrumbs' }) + .getByText(hostSetName) + .click(); + await page.getByRole('link', { name: 'Hosts' }).click(); + } + + // 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); + + // Add another host source + await createHostCatalog(page); + const newHostSetName = await createHostSet(page); + await page + .getByRole('navigation', { name: 'Resources' }) + .getByRole('link', { name: 'Targets' }) + .click(); + await page.getByRole('link', { name: targetName }).click(); + await addHostSourceToTarget(page, newHostSetName); + + // Remove the host source from the target + await page + .getByRole('link', { name: newHostSetName }) + .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(); + + // Add the host source back + await addHostSourceToTarget(page, newHostSetName); + } finally { + 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, + ); + + if (orgName) { + const orgs = JSON.parse(execSync('boundary scopes list -format json')); + const org = orgs.items.filter((obj) => obj.name == orgName)[0]; + if (org) { + await deleteOrgCli(org.id); + } + } } - - // 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); - - // Add another host source - await createHostCatalog(page); - const newHostSetName = await createHostSet(page); - await page - .getByRole('navigation', { name: 'Resources' }) - .getByRole('link', { name: 'Targets' }) - .click(); - await page.getByRole('link', { name: targetName }).click(); - await addHostSourceToTarget(page, newHostSetName); - - // Remove the host source from the target - await page - .getByRole('link', { name: newHostSetName }) - .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(); - - // Add the host source back - await addHostSourceToTarget(page, newHostSetName); }); }); diff --git a/ui/admin/tests/e2e/tests/pagination.spec.js b/ui/admin/tests/e2e/tests/pagination.spec.js index ea864be52..ed9e22f90 100644 --- a/ui/admin/tests/e2e/tests/pagination.spec.js +++ b/ui/admin/tests/e2e/tests/pagination.spec.js @@ -14,7 +14,6 @@ test('Search and Pagination (Targets) @ce @ent @aws @docker', async ({ page, request, }) => { - await page.goto('/'); let org; try { const newOrg = await request.post(`/v1/scopes`, { @@ -52,6 +51,7 @@ test('Search and Pagination (Targets) @ce @ent @aws @docker', async ({ } // Navigate to targets page + await page.goto('/'); await page.getByRole('link', { name: org.name }).click(); await page.getByRole('link', { name: project.name }).click(); await page 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 ef22304c4..1604640b1 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 @@ -17,7 +17,6 @@ const { deletePolicyCli, } = require('../helpers/boundary-cli'); const { - createSshTargetWithAddressAndWorkerFilterEnt, waitForSessionToBeVisible, createStorageBucketAws, enableSessionRecording, @@ -27,7 +26,9 @@ const { createProject, createStaticCredentialStore, createStaticCredentialKeyPair, + createSshTargetWithAddressEnt, addInjectedCredentialsToTarget, + addEgressWorkerFilterToTarget, } = require('../helpers/boundary-ui'); test.use({ storageState: authenticatedState }); @@ -98,10 +99,13 @@ 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 createSshTargetWithAddressAndWorkerFilterEnt( + const targetName = await createSshTargetWithAddressEnt( page, process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, + ); + await addEgressWorkerFilterToTarget( + page, `"${process.env.E2E_WORKER_TAG_EGRESS}" in "/tags/type"`, ); const targets = JSON.parse( 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 4df426e04..5bc2d256b 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 @@ -17,7 +17,6 @@ const { deletePolicyCli, } = require('../helpers/boundary-cli'); const { - createSshTargetWithAddressAndWorkerFilterEnt, waitForSessionToBeVisible, createStorageBucketMinio, enableSessionRecording, @@ -27,7 +26,9 @@ const { createProject, createStaticCredentialStore, createStaticCredentialKeyPair, + createSshTargetWithAddressEnt, addInjectedCredentialsToTarget, + addEgressWorkerFilterToTarget, } = require('../helpers/boundary-ui'); test.use({ storageState: authenticatedState }); @@ -100,12 +101,16 @@ 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 createSshTargetWithAddressAndWorkerFilterEnt( + const targetName = await createSshTargetWithAddressEnt( page, process.env.E2E_TARGET_ADDRESS, process.env.E2E_TARGET_PORT, + ); + await addEgressWorkerFilterToTarget( + page, `"${process.env.E2E_WORKER_TAG_EGRESS}" in "/tags/type"`, ); + const targets = JSON.parse( execSync('boundary targets list -format json -scope-id ' + projectId), ); 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 df1361aad..627294a88 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 @@ -39,7 +39,6 @@ test.beforeAll(async () => { 'E2E_TARGET_ADDRESS', 'E2E_SSH_USER', 'E2E_SSH_KEY_PATH', - 'E2E_WORKER_TAG_EGRESS', ]); await checkBoundaryCli(); diff --git a/ui/admin/tests/e2e/tests/target-ent.spec.js b/ui/admin/tests/e2e/tests/target-ent.spec.js index a950b75b8..873ff7948 100644 --- a/ui/admin/tests/e2e/tests/target-ent.spec.js +++ b/ui/admin/tests/e2e/tests/target-ent.spec.js @@ -38,7 +38,6 @@ test.beforeAll(async () => { 'E2E_SSH_KEY_PATH', 'E2E_TARGET_ADDRESS', 'E2E_TARGET_PORT', - 'E2E_WORKER_TAG_EGRESS', ]); await checkBoundaryCli(); diff --git a/ui/admin/tests/e2e/tests/target.spec.js b/ui/admin/tests/e2e/tests/target.spec.js index bcb4c5264..b8de752d9 100644 --- a/ui/admin/tests/e2e/tests/target.spec.js +++ b/ui/admin/tests/e2e/tests/target.spec.js @@ -23,6 +23,7 @@ const { createTargetWithAddress, waitForSessionToBeVisible, addHostSourceToTarget, + addEgressWorkerFilterToTarget, } = require('../helpers/boundary-ui'); test.use({ storageState: authenticatedState }); @@ -195,15 +196,14 @@ test('Verify TCP target is updated @ce @aws @docker', async ({ page }) => { await page.getByLabel('Default Client Port').fill('10'); await page.getByLabel('Maximum Duration').fill('1000'); await page.getByLabel('Maximum Connections').fill('10'); - await page.getByLabel('Egress worker filter').click(); - await page - .getByRole('textbox', { name: /^Filter/, exact: true }) - .fill('"dev" in "/tags/type"'); 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 addEgressWorkerFilterToTarget(page, '"dev" in "/tags/type"'); } finally { await authenticateBoundaryCli( process.env.BOUNDARY_ADDR,