From 8874d0be866059394dd5c2fc87c34c11a8049d85 Mon Sep 17 00:00:00 2001 From: Zhihe Li Date: Tue, 10 Dec 2024 12:30:57 -0500 Subject: [PATCH 1/5] =?UTF-8?q?docs:=20=E2=9C=8F=EF=B8=8F=20Add=20to=20rea?= =?UTF-8?q?dme=20how=20to=20run=20admin=20e2e=20against=20local=20branch?= =?UTF-8?q?=20(#2616)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * docs: ✏️ Add to readme how to run admin e2e against local branch * chore: 🤖 Add packageManager field for corepack users --- e2e-tests/README.md | 13 +++++++++++++ package.json | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/e2e-tests/README.md b/e2e-tests/README.md index abacd66910..360e4566f5 100644 --- a/e2e-tests/README.md +++ b/e2e-tests/README.md @@ -97,6 +97,19 @@ Enos needs some configuration variables to run the scenario successfully. [See t More documentation about [scenario variables](https://github.com/hashicorp/boundary/tree/main/enos#scenarios-variables). +> [!TIP] +> To run e2e tests against a local branch for Admin UI, update the UI commit in boundary so it gets picked up when enos builds the binary + +By default, enos will build the version of the UI that is specified in the `VERSION` commit for `boundary`. To use a specific branch, update the version first before running the scenario. + +In the `boundary` or `boundary-enterprise` repository: +```bash +UI_COMMITISH= make update-ui-version + +# Alternatively if you want to just be able to run the same command for the most recent commit +UI_COMMITISH=$(git -C rev-parse HEAD) make update-ui-version +``` + ## Run tests: Before running the e2e test locally, we need to launch an Enos Scenario. Make sure you followed all the steps within the [Getting started section](#getting-started). diff --git a/package.json b/package.json index bbe1b12587..d5167826f2 100755 --- a/package.json +++ b/package.json @@ -63,5 +63,6 @@ }, "engines": { "node": "18.* || 20.*" - } + }, + "packageManager": "yarn@1.22.19+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447" } From 078e36f9b87777327858532864150dac93db4b22 Mon Sep 17 00:00:00 2001 From: Cameron Perera Date: Tue, 10 Dec 2024 14:12:43 -0600 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=F0=9F=90=9B=20update=20@name=20attr?= =?UTF-8?q?ibute=20for=20RadioCard=20Groups=20(#2617)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-15676 --- addons/core/translations/form/en-us.yaml | 2 ++ ui/admin/app/components/form/alias/index.hbs | 6 +++++- .../form/credential-library/vault-generic/index.hbs | 6 +++++- .../credential-library/vault-ssh-certificate/index.hbs | 6 +++++- .../components/form/credential-store/static/index.hbs | 7 +++++-- .../app/components/form/credential-store/vault/index.hbs | 7 +++++-- ui/admin/app/components/form/credential/json/index.hbs | 3 +-- .../components/form/credential/ssh_private_key/index.hbs | 3 +-- .../form/credential/username_password/index.hbs | 3 +-- ui/admin/app/components/form/host-catalog/aws/index.hbs | 8 ++++++-- .../app/components/form/host-catalog/azure/index.hbs | 8 ++++++-- .../app/components/form/host-catalog/static/index.hbs | 2 +- .../app/components/form/storage-bucket/aws/index.hbs | 9 ++++++--- .../app/components/form/storage-bucket/minio/index.hbs | 6 +++++- ui/admin/app/components/form/target/details/index.hbs | 6 +++++- ui/admin/tests/acceptance/host-catalogs/create-test.js | 2 +- ui/admin/tests/acceptance/storage-buckets/create-test.js | 8 ++------ ui/admin/tests/acceptance/storage-buckets/selectors.js | 2 +- ui/admin/tests/acceptance/targets/create-test.js | 2 +- 19 files changed, 64 insertions(+), 32 deletions(-) diff --git a/addons/core/translations/form/en-us.yaml b/addons/core/translations/form/en-us.yaml index 125d73e964..d3cfc20d05 100644 --- a/addons/core/translations/form/en-us.yaml +++ b/addons/core/translations/form/en-us.yaml @@ -18,6 +18,8 @@ description: type: label: Type help: The type of the resource, to help differentiate schemas +plugin_type: + label: plugin_type login_name: label: Login Name help: The account login name diff --git a/ui/admin/app/components/form/alias/index.hbs b/ui/admin/app/components/form/alias/index.hbs index 0caca3daf1..a36ba0aa5f 100644 --- a/ui/admin/app/components/form/alias/index.hbs +++ b/ui/admin/app/components/form/alias/index.hbs @@ -56,7 +56,11 @@ {{t 'resources.alias.description'}} - + {{t 'resources.target.form.type.label'}} {{t 'resources.alias.form.type.label'}} diff --git a/ui/admin/app/components/form/credential-library/vault-generic/index.hbs b/ui/admin/app/components/form/credential-library/vault-generic/index.hbs index cf6e8f5aa4..a88e431c3c 100644 --- a/ui/admin/app/components/form/credential-library/vault-generic/index.hbs +++ b/ui/admin/app/components/form/credential-library/vault-generic/index.hbs @@ -52,7 +52,11 @@ {{#if (and @model.isNew (feature-flag 'ssh-target'))}} - + {{t 'form.type.label'}} {{#each this.types as |type|}} {{#if @model.isNew}} - + {{t 'form.type.label'}} {{#each this.types as |type|}} + {{t 'form.type.label'}} {{#each-in this.mapResourceTypeWithIcon as |credentialStoreType icon|}} + {{t 'form.type.label'}} {{#each-in this.mapResourceTypeWithIcon as |credentialStoreType icon|}} {{#if @model.isNew}} - + {{t 'form.type.label'}} {{#each @types as |credentialType|}} {{#if @model.isNew}} - + {{t 'form.type.label'}} {{#each @types as |credentialType|}} {{#if @model.isNew}} - + {{t 'form.type.label'}} {{#each @types as |credentialType|}} {{#if @model.isNew}} - + {{t 'form.type.label'}} @@ -83,7 +83,11 @@ {{/each}} - + {{t 'titles.provider'}} diff --git a/ui/admin/app/components/form/host-catalog/azure/index.hbs b/ui/admin/app/components/form/host-catalog/azure/index.hbs index 9e8fa239b0..23c3ac923f 100644 --- a/ui/admin/app/components/form/host-catalog/azure/index.hbs +++ b/ui/admin/app/components/form/host-catalog/azure/index.hbs @@ -50,7 +50,7 @@ {{#if @model.isNew}} - + {{t 'form.type.label'}} {{#each this.hostCatalogTypes as |hostCatalogType|}} {{/each}} - + {{t 'titles.provider'}} {{t 'titles.choose-a-provider'}} {{concat diff --git a/ui/admin/app/components/form/host-catalog/static/index.hbs b/ui/admin/app/components/form/host-catalog/static/index.hbs index 4f28b4ae29..4c7b3b855d 100644 --- a/ui/admin/app/components/form/host-catalog/static/index.hbs +++ b/ui/admin/app/components/form/host-catalog/static/index.hbs @@ -51,7 +51,7 @@ {{#if @model.isNew}} - + {{t 'form.type.label'}} {{#each this.hostCatalogTypes as |hostCatalogType|}} + {{t 'titles.provider'}} {{t 'descriptions.choose-a-provider'}} @@ -212,11 +216,10 @@ {{! credential_type }} - + {{t 'resources.storage-bucket.types.credential'}} {{#each this.credentials as |credentialType|}} + {{t 'titles.provider'}} {{t 'descriptions.choose-a-provider'}} diff --git a/ui/admin/app/components/form/target/details/index.hbs b/ui/admin/app/components/form/target/details/index.hbs index c9d934dc7a..2b67648821 100644 --- a/ui/admin/app/components/form/target/details/index.hbs +++ b/ui/admin/app/components/form/target/details/index.hbs @@ -53,7 +53,11 @@ {{#if (and @model.isNew (feature-flag 'ssh-target'))}} - + {{t 'resources.target.form.type.label'}} {{t 'resources.target.form.type.help'}} {{#each this.typeMetas as |type|}} diff --git a/ui/admin/tests/acceptance/host-catalogs/create-test.js b/ui/admin/tests/acceptance/host-catalogs/create-test.js index 95aaaaa85e..71fbd40dba 100644 --- a/ui/admin/tests/acceptance/host-catalogs/create-test.js +++ b/ui/admin/tests/acceptance/host-catalogs/create-test.js @@ -21,7 +21,7 @@ module('Acceptance | host-catalogs | create', function (hooks) { const NAME_INPUT_SELECTOR = '[name="name"]'; const DESCRIPTION_INPUT_SELECTOR = '[name="description"]'; - const TYPE_INPUT_SELECTOR = '[name="type"]'; + const TYPE_INPUT_SELECTOR = '[name="Type"]'; const SAVE_BUTTON_SELECTOR = '[type="submit"]'; const CANCEL_BUTTON_SELECTOR = '.rose-form-actions [type="button"]'; const ALERT_TEXT_SELECTOR = diff --git a/ui/admin/tests/acceptance/storage-buckets/create-test.js b/ui/admin/tests/acceptance/storage-buckets/create-test.js index bf2ea9ac4b..9d10a4f461 100644 --- a/ui/admin/tests/acceptance/storage-buckets/create-test.js +++ b/ui/admin/tests/acceptance/storage-buckets/create-test.js @@ -148,9 +148,7 @@ module('Acceptance | storage-buckets | create', function (hooks) { await fillIn(commonSelectors.FIELD_NAME, commonSelectors.FIELD_NAME_VALUE); // There are 2 credential types - assert - .dom(`${selectors.GROUP_CREDENTIAL_TYPE} .hds-form-radio-card`) - .exists({ count: 2 }); + assert.dom(selectors.GROUP_CREDENTIAL_TYPE).exists({ count: 2 }); await click(selectors.FIELD_DYNAMIC_CREDENTIAL); await fillIn(selectors.FIELD_ROLE_ARN, selectors.FIELD_ROLE_ARN_VALUE); @@ -172,9 +170,7 @@ module('Acceptance | storage-buckets | create', function (hooks) { await click(`[href="${urls.newStorageBucket}"]`); await fillIn(commonSelectors.FIELD_NAME, commonSelectors.FIELD_NAME_VALUE); - assert - .dom(`${selectors.GROUP_CREDENTIAL_TYPE} .hds-form-radio-card`) - .exists({ count: 2 }); + assert.dom(selectors.GROUP_CREDENTIAL_TYPE).exists({ count: 2 }); await click(selectors.FIELD_STATIC_CREDENTIAL); await fillIn(selectors.FIELD_ACCESS_KEY, selectors.FIELD_ACCESS_KEY_VALUE); diff --git a/ui/admin/tests/acceptance/storage-buckets/selectors.js b/ui/admin/tests/acceptance/storage-buckets/selectors.js index 08eef815a4..c86b91f70c 100644 --- a/ui/admin/tests/acceptance/storage-buckets/selectors.js +++ b/ui/admin/tests/acceptance/storage-buckets/selectors.js @@ -14,7 +14,7 @@ export const FIELD_BUCKET_NAME_VALUE = 'Test Bucket name'; export const FIELD_BUCKET_NAME_ERROR = '[data-test-bucket-name-error]'; export const FIELD_BUCKET_PREFIX = '[name=bucket_prefix]'; export const FIELD_REGION = '[name=region]'; -export const GROUP_CREDENTIAL_TYPE = '[name=credential_type]'; +export const GROUP_CREDENTIAL_TYPE = '[name=Type]'; export const FIELD_STATIC_CREDENTIAL = '[value=static]'; export const FIELD_DYNAMIC_CREDENTIAL = '[value=dynamic]'; export const FIELD_ROLE_ARN = '[name=role_arn]'; diff --git a/ui/admin/tests/acceptance/targets/create-test.js b/ui/admin/tests/acceptance/targets/create-test.js index 4e7233bc25..0baf0fceb2 100644 --- a/ui/admin/tests/acceptance/targets/create-test.js +++ b/ui/admin/tests/acceptance/targets/create-test.js @@ -85,7 +85,7 @@ module('Acceptance | targets | create', function (hooks) { test('defaults to type `ssh` when no query param provided', async function (assert) { featuresService.enable('ssh-target'); await visit(urls.newTarget); - assert.strictEqual(find('[name="type"]:checked').value, TYPE_TARGET_SSH); + assert.strictEqual(find('[name="Type"]:checked').value, TYPE_TARGET_SSH); }); test('can create a type `ssh` target', async function (assert) { From e16b4e68c8bc8ea901663aa800bf5e8f27f58182 Mon Sep 17 00:00:00 2001 From: Zhihe Li Date: Tue, 10 Dec 2024 16:11:12 -0500 Subject: [PATCH 3/5] Icu 14778 add more sessions tests (#2615) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test: 💍 Add desktop sessions tests ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14778 * chore: 🤖 Add support for dotenv and stop using relative paths ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14778 * refactor: 💡 Force usage of node 20 ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14778 --- e2e-tests/.gitignore | 2 + e2e-tests/desktop/fixtures/electronTest.js | 8 +- e2e-tests/desktop/playwright.config.js | 1 - e2e-tests/desktop/tests/sessions.spec.js | 251 +++++++++++++++++++++ e2e-tests/desktop/tests/targets.spec.js | 53 +++-- e2e-tests/global-setup.js | 12 +- e2e-tests/package.json | 2 +- 7 files changed, 301 insertions(+), 28 deletions(-) create mode 100644 e2e-tests/desktop/tests/sessions.spec.js diff --git a/e2e-tests/.gitignore b/e2e-tests/.gitignore index 72f0a85150..fc8ad50f76 100644 --- a/e2e-tests/.gitignore +++ b/e2e-tests/.gitignore @@ -2,3 +2,5 @@ admin/artifacts desktop/artifacts playwright-report + +.auth \ No newline at end of file diff --git a/e2e-tests/desktop/fixtures/electronTest.js b/e2e-tests/desktop/fixtures/electronTest.js index 729f656bf7..0866dced36 100644 --- a/e2e-tests/desktop/fixtures/electronTest.js +++ b/e2e-tests/desktop/fixtures/electronTest.js @@ -13,7 +13,13 @@ import fs from 'fs'; * @return {string} */ const getExecutablePath = (buildDirectory = 'out') => { - const rootDir = path.resolve('../ui/desktop/electron-app'); + // Using process.cwd() can change depending on where you run the tests so we use the current file location + // __dirname isn't available in ES modules so we need to indirectly get the directory + const rootDir = path.resolve( + import.meta.dirname, + '../../../ui/desktop/electron-app', + ); + // directory where the builds are stored const outDir = path.resolve(rootDir, buildDirectory); // list of files in the out directory diff --git a/e2e-tests/desktop/playwright.config.js b/e2e-tests/desktop/playwright.config.js index f361a087a2..726c5da4f6 100644 --- a/e2e-tests/desktop/playwright.config.js +++ b/e2e-tests/desktop/playwright.config.js @@ -11,7 +11,6 @@ export default defineConfig({ workers: 1, // Tests need to be run in serial, otherwise there may be conflicts when using the CLI use: { baseURL: process.env.BOUNDARY_ADDR, - storageState: './admin/artifacts/authenticated-state.json', extraHTTPHeaders: { // This token is set in global-setup.js Authorization: `Bearer ${process.env.E2E_TOKEN}`, diff --git a/e2e-tests/desktop/tests/sessions.spec.js b/e2e-tests/desktop/tests/sessions.spec.js new file mode 100644 index 0000000000..5692774d9b --- /dev/null +++ b/e2e-tests/desktop/tests/sessions.spec.js @@ -0,0 +1,251 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: BUSL-1.1 + */ + +import { expect, test } from '../fixtures/baseTest.js'; +import * as boundaryHttp from '../../helpers/boundary-http.js'; + +let org; +let targetWithHost; +let sshTarget; +let sshTarget2; +let credential; + +test.beforeEach( + async ({ request, targetAddress, targetPort, sshUser, sshKeyPath }) => { + org = await boundaryHttp.createOrg(request); + const project = await boundaryHttp.createProject(request, org.id); + + // Create host set + const hostCatalog = await boundaryHttp.createStaticHostCatalog( + request, + project.id, + ); + const host = await boundaryHttp.createHost(request, { + hostCatalogId: hostCatalog.id, + address: targetAddress, + }); + + const hostSet = await boundaryHttp.createHostSet(request, hostCatalog.id); + await boundaryHttp.addHostToHostSet(request, { + hostSet, + hostIds: [host.id], + }); + + // Create a static credential store and key pair credential + const credentialStore = await boundaryHttp.createStaticCredentialStore( + request, + project.id, + ); + credential = await boundaryHttp.createStaticCredentialKeyPair(request, { + credentialStoreId: credentialStore.id, + username: sshUser, + sshKeyPath, + }); + + // Create tcp target with host set and 1 host + targetWithHost = await boundaryHttp.createTarget(request, { + scopeId: project.id, + type: 'tcp', + port: targetPort, + }); + targetWithHost = await boundaryHttp.addHostSource(request, { + target: targetWithHost, + hostSourceIds: [hostSet.id], + }); + targetWithHost = await boundaryHttp.addBrokeredCredentials(request, { + target: targetWithHost, + credentialIds: [credential.id], + }); + + // Create SSH targets with address + sshTarget = await boundaryHttp.createTarget(request, { + scopeId: project.id, + type: 'ssh', + port: targetPort, + address: targetAddress, + }); + sshTarget = await boundaryHttp.addInjectedCredentials(request, { + target: sshTarget, + credentialIds: [credential.id], + }); + + sshTarget2 = await boundaryHttp.createTarget(request, { + scopeId: project.id, + type: 'ssh', + port: targetPort, + address: targetAddress, + }); + sshTarget2 = await boundaryHttp.addInjectedCredentials(request, { + target: sshTarget2, + credentialIds: [credential.id], + }); + }, +); + +test.afterEach(async ({ request }) => { + if (org) { + await boundaryHttp.deleteOrg(request, org.id); + } +}); + +test.describe('Sessions tests', async () => { + test('Establishes two different sessions and cancels them', async ({ + authedPage, + }) => { + // Connect to two targets + await authedPage + .getByRole('row', { name: sshTarget.name }) + .getByRole('link', { name: 'Connect' }) + .click(); + await expect( + authedPage.getByRole('heading', { name: 'Sessions' }), + ).toBeVisible(); + + await authedPage.getByRole('link', { name: 'Targets' }).click(); + + await authedPage + .getByRole('row', { name: targetWithHost.name }) + .getByRole('link', { name: 'Connect' }) + .click(); + await expect( + authedPage.getByRole('heading', { name: 'Sessions' }), + ).toBeVisible(); + + await authedPage.getByRole('link', { name: 'Sessions' }).click(); + + await expect( + authedPage + .getByRole('row') + .filter({ hasNot: authedPage.getByRole('columnheader') }) + .getByText('Pending'), + ).toHaveCount(2); + + // Cancel both sessions + for (const cancel of await authedPage.getByLabel('Cancel').all()) { + await cancel.click(); + } + + await expect( + authedPage + .getByRole('row') + .filter({ hasNot: authedPage.getByRole('columnheader') }) + .getByText('Canceling'), + ).toHaveCount(2); + }); +}); + +test.describe('Filtering sessions tests', async () => { + test.beforeEach(async ({ authedPage }) => { + // Connect to three targets + await authedPage + .getByRole('row', { name: sshTarget.name }) + .getByRole('link', { name: 'Connect' }) + .click(); + await expect( + authedPage.getByRole('heading', { name: 'Sessions' }), + ).toBeVisible(); + + await authedPage.getByRole('link', { name: 'Targets' }).click(); + + await authedPage + .getByRole('row', { name: sshTarget2.name }) + .getByRole('link', { name: 'Connect' }) + .click(); + await expect( + authedPage.getByRole('heading', { name: 'Sessions' }), + ).toBeVisible(); + + await authedPage.getByRole('link', { name: 'Targets' }).click(); + + await authedPage + .getByRole('row', { name: targetWithHost.name }) + .getByRole('link', { name: 'Connect' }) + .click(); + await expect( + authedPage.getByRole('heading', { name: 'Sessions' }), + ).toBeVisible(); + await authedPage.getByRole('link', { name: 'Sessions' }).click(); + + await expect( + authedPage + .getByRole('row') + .filter({ hasNot: authedPage.getByRole('columnheader') }) + .getByRole('cell', { name: 'Pending' }), + ).toHaveCount(3); + }); + + test.afterEach(async ({ authedPage }) => { + await authedPage.getByRole('button', { name: 'Clear Filters' }).click(); + await expect( + authedPage.getByRole('button', { name: 'Clear Filters' }), + ).toBeHidden(); + for (const cancel of await authedPage.getByLabel('Cancel').all()) { + await cancel.click(); + } + + await expect(authedPage.getByLabel('Cancel')).toHaveCount(0); + }); + + test('Filters by target', async ({ authedPage }) => { + await authedPage.getByRole('button', { name: 'Target' }).click(); + await authedPage.getByLabel('Narrow results').fill(targetWithHost.name); + await expect(authedPage.getByRole('checkbox')).toHaveCount(1); + await authedPage.getByLabel(targetWithHost.name).check(); + await authedPage.getByRole('button', { name: 'Apply' }).click(); + + await expect( + authedPage + .getByRole('row') + .filter({ hasNot: authedPage.getByRole('columnheader') }) + .getByRole('cell', { name: 'Pending' }), + ).toHaveCount(1); + await expect( + authedPage.getByRole('cell', { name: targetWithHost.name }), + ).toBeVisible(); + }); + + test('Filters by status', async ({ authedPage }) => { + await authedPage + .getByRole('row', { name: sshTarget.name }) + .getByRole('link') + .first() + .click(); + await authedPage.getByRole('tab', { name: 'Shell' }).click(); + await authedPage.waitForTimeout(3000); + await authedPage.getByRole('link', { name: 'Sessions' }).click(); + + await authedPage.getByRole('button', { name: 'Clear Filters' }).click(); + await expect( + authedPage.getByRole('button', { name: 'Clear Filters' }), + ).toBeHidden(); + await authedPage.getByRole('button', { name: 'Status' }).click(); + await authedPage.getByLabel('Active').check(); + await authedPage.getByRole('button', { name: 'Apply' }).click(); + + await expect( + authedPage + .getByRole('row') + .filter({ hasNot: authedPage.getByRole('columnheader') }) + .getByRole('cell', { name: 'Active' }), + ).toHaveCount(1); + await expect( + authedPage.getByRole('cell', { name: sshTarget.name }), + ).toBeVisible(); + + await authedPage.getByRole('button', { name: 'Clear Filters' }).click(); + await expect( + authedPage.getByRole('button', { name: 'Clear Filters' }), + ).toBeHidden(); + await authedPage.getByRole('button', { name: 'Status' }).click(); + await authedPage.getByLabel('Pending').check(); + await authedPage.getByRole('button', { name: 'Apply' }).click(); + await expect( + authedPage + .getByRole('row') + .filter({ hasNot: authedPage.getByRole('columnheader') }) + .getByRole('cell', { name: 'Pending' }), + ).toHaveCount(2); + }); +}); diff --git a/e2e-tests/desktop/tests/targets.spec.js b/e2e-tests/desktop/tests/targets.spec.js index 12f87bf081..b8b532cc75 100644 --- a/e2e-tests/desktop/tests/targets.spec.js +++ b/e2e-tests/desktop/tests/targets.spec.js @@ -4,7 +4,7 @@ */ import { expect, test } from '../fixtures/baseTest.js'; -import * as boundaryHttp from '../../helpers/boundary-http'; +import * as boundaryHttp from '../../helpers/boundary-http.js'; const hostName = 'Host name for test'; let org; @@ -13,7 +13,7 @@ let targetWithTwoHosts; let sshTarget; let credential; -test.beforeAll( +test.beforeEach( async ({ request, targetAddress, targetPort, sshUser, sshKeyPath }) => { org = await boundaryHttp.createOrg(request); const project = await boundaryHttp.createProject(request, org.id); @@ -108,20 +108,20 @@ test.beforeAll( }, ); -test.afterAll(async ({ request }) => { +test.afterEach(async ({ request }) => { if (org) { await boundaryHttp.deleteOrg(request, org.id); } }); -test.describe('Targets test', async () => { +test.describe('Targets tests', async () => { test('Connects to a tcp target with one host', async ({ authedPage }) => { await authedPage.getByRole('link', { name: targetWithHost.name }).click(); await authedPage.getByRole('button', { name: 'Connect' }).click(); - await expect(authedPage).toHaveURL( - /.*\/scopes\/global\/projects\/sessions/, - ); + await expect( + authedPage.getByRole('heading', { name: 'Sessions' }), + ).toBeVisible(); await expect(authedPage.getByText(credential.name)).toBeVisible(); await expect(authedPage.getByText('username')).toBeVisible(); @@ -153,9 +153,9 @@ test.describe('Targets test', async () => { await authedPage.getByRole('button', { name: 'End Session' }).click(); await expect(authedPage.getByText('Canceled successfully.')).toBeVisible(); - await expect(authedPage).toHaveURL( - /.*\/scopes\/global\/projects\/targets$/, - ); + await expect( + authedPage.getByRole('heading', { name: 'Targets' }), + ).toBeVisible(); }); [{ host: 'Quick Connect' }, { host: hostName }].forEach(({ host }) => { @@ -167,14 +167,15 @@ test.describe('Targets test', async () => { .click(); await authedPage.getByRole('button', { name: 'Connect' }).click(); - await expect(authedPage).toHaveURL( - /.*\/scopes\/global\/projects\/targets/, - ); + await expect( + authedPage.getByRole('heading', { name: 'Choose a Host' }), + ).toBeVisible(); + await authedPage.getByRole('button', { name: host }).click(); - await expect(authedPage).toHaveURL( - /.*\/scopes\/global\/projects\/sessions/, - ); + await expect( + authedPage.getByRole('heading', { name: 'Sessions' }), + ).toBeVisible(); await expect(authedPage.getByText(credential.name)).toBeVisible(); await expect(authedPage.getByText('username')).toBeVisible(); @@ -218,9 +219,9 @@ test.describe('Targets test', async () => { await authedPage.getByRole('link', { name: sshTarget.name }).click(); await authedPage.getByRole('button', { name: 'Connect' }).click(); - await expect(authedPage).toHaveURL( - /.*\/scopes\/global\/projects\/sessions/, - ); + await expect( + authedPage.getByRole('heading', { name: 'Sessions' }), + ).toBeVisible(); await authedPage.getByRole('tab', { name: 'Shell' }).click(); // TODO: Research a better way to test canvas elements for the shell, @@ -228,16 +229,20 @@ test.describe('Targets test', async () => { await authedPage.getByRole('button', { name: 'End Session' }).click(); await expect(authedPage.getByText('Canceled successfully.')).toBeVisible(); - await expect(authedPage).toHaveURL( - /.*\/scopes\/global\/projects\/targets$/, - ); + + await expect( + authedPage.getByRole('heading', { name: 'Targets' }), + ).toBeVisible(); }); test('Searches targets correctly', async ({ authedPage }) => { await authedPage.getByLabel('Search').fill(targetWithHost.name); - // One row is the header - await expect(authedPage.getByRole('row')).toHaveCount(2); + await expect( + authedPage + .getByRole('row') + .filter({ hasNot: authedPage.getByRole('columnheader') }), + ).toHaveCount(1); await expect( authedPage.getByRole('link', { name: targetWithHost.name }), ).toBeVisible(); diff --git a/e2e-tests/global-setup.js b/e2e-tests/global-setup.js index 128e233d44..bf8560c558 100644 --- a/e2e-tests/global-setup.js +++ b/e2e-tests/global-setup.js @@ -6,6 +6,13 @@ import { chromium, test as baseTest } from '@playwright/test'; import { checkEnv } from './helpers/general.js'; import { LoginPage } from './admin/pages/login.js'; +import path from 'path'; +import dotenv from 'dotenv'; + +const __dirname = import.meta.dirname; + +// Import environment variables from .env file if available +dotenv.config({ path: path.resolve(__dirname, './.env') }); async function globalSetup() { await checkEnv([ @@ -42,7 +49,10 @@ const authenticateToBoundary = async () => { export default globalSetup; -export const authenticatedState = './admin/artifacts/authenticated-state.json'; +export const authenticatedState = path.resolve( + __dirname, + './.auth/authenticated-state.json', +); // Centralized location for environment variables used in tests export const test = baseTest.extend({ diff --git a/e2e-tests/package.json b/e2e-tests/package.json index 19195fab81..ae4fc2e05a 100644 --- a/e2e-tests/package.json +++ b/e2e-tests/package.json @@ -38,7 +38,7 @@ "prettier": "^3.0.0" }, "engines": { - "node": "18.* || 20.*" + "node": "20.* || 22.*" }, "lint-staged": { "*.js": "eslint --fix", From 0b9c235dd274a381edd2a0b31f8efc974e4020b5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:53:38 -0500 Subject: [PATCH 4/5] chore(deps-dev): bump nanoid from 5.0.8 to 5.0.9 (#2620) Bumps [nanoid](https://github.com/ai/nanoid) from 5.0.8 to 5.0.9. - [Release notes](https://github.com/ai/nanoid/releases) - [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md) - [Commits](https://github.com/ai/nanoid/compare/5.0.8...5.0.9) --- updated-dependencies: - dependency-name: nanoid dependency-type: direct:development ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Zhihe Li --- e2e-tests/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/e2e-tests/package.json b/e2e-tests/package.json index ae4fc2e05a..21c64b4420 100644 --- a/e2e-tests/package.json +++ b/e2e-tests/package.json @@ -34,7 +34,7 @@ "eslint": "^8.54.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-n": "^16.3.1", - "nanoid": "^5.0.8", + "nanoid": "^5.0.9", "prettier": "^3.0.0" }, "engines": { diff --git a/yarn.lock b/yarn.lock index bad89c078e..81915039b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14092,10 +14092,10 @@ nanoid@^3.3.6: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== -nanoid@^5.0.8: - version "5.0.8" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.8.tgz#7610003f6b3b761b5c244bb342c112c5312512bf" - integrity sha512-TcJPw+9RV9dibz1hHUzlLVy8N4X9TnwirAjrU08Juo6BNKggzVfP2ZJ/3ZUSq15Xl5i85i+Z89XBO90pB2PghQ== +nanoid@^5.0.9: + version "5.0.9" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.9.tgz#977dcbaac055430ce7b1e19cf0130cea91a20e50" + integrity sha512-Aooyr6MXU6HpvvWXKoVoXwKMs/KyVakWwg7xQfv5/S/RIgJMy0Ifa45H9qqYy7pTCszrHzP21Uk4PZq2HpEM8Q== natural-compare@^1.4.0: version "1.4.0" From 0623d4ad47366d2961bf38865e122de9b14a024c Mon Sep 17 00:00:00 2001 From: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Tue, 10 Dec 2024 16:24:25 -0800 Subject: [PATCH 5/5] Fix e2e test page selectors due to hds refactor (#2610) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 🤖 fix e2e broken tests due to hds refactor * chore: 🤖 remove unneeded change & fix another selector * chore: 🤖 upgrade playwright * chore: 🤖 remove unneeded dependency in desktop ui --- e2e-tests/admin/pages/auth-methods.js | 1 - e2e-tests/admin/pages/groups.js | 1 - e2e-tests/admin/pages/roles.js | 1 - e2e-tests/admin/pages/targets.js | 46 +++++++++------------------ e2e-tests/admin/pages/users.js | 1 - e2e-tests/package.json | 2 +- ui/admin/package.json | 1 - ui/desktop/package.json | 2 -- yarn.lock | 28 ++++++++-------- 9 files changed, 30 insertions(+), 53 deletions(-) diff --git a/e2e-tests/admin/pages/auth-methods.js b/e2e-tests/admin/pages/auth-methods.js index 8c333ba4aa..280228c839 100644 --- a/e2e-tests/admin/pages/auth-methods.js +++ b/e2e-tests/admin/pages/auth-methods.js @@ -70,7 +70,6 @@ export class AuthMethodsPage extends BaseResourcePage { 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); diff --git a/e2e-tests/admin/pages/groups.js b/e2e-tests/admin/pages/groups.js index 7b588e1bd2..0d6fde5300 100644 --- a/e2e-tests/admin/pages/groups.js +++ b/e2e-tests/admin/pages/groups.js @@ -39,7 +39,6 @@ export class GroupsPage extends BaseResourcePage { 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(); diff --git a/e2e-tests/admin/pages/roles.js b/e2e-tests/admin/pages/roles.js index 96d38240ab..f9937a2bd2 100644 --- a/e2e-tests/admin/pages/roles.js +++ b/e2e-tests/admin/pages/roles.js @@ -41,7 +41,6 @@ export class RolesPage extends BaseResourcePage { .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(); diff --git a/e2e-tests/admin/pages/targets.js b/e2e-tests/admin/pages/targets.js index 5d6481bbe8..c007a13186 100644 --- a/e2e-tests/admin/pages/targets.js +++ b/e2e-tests/admin/pages/targets.js @@ -251,18 +251,14 @@ export class TargetsPage extends BaseResourcePage { ).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 { + if (!emptyLinkIsVisible) { await this.page.getByText('Manage').click(); - await this.page.getByRole('link', { name: 'Add Host Sources' }).click(); } + await this.page + .getByRole('link', { name: 'Add Host Sources', exact: true }) + .click(); await this.page .getByRole('cell', { name: hostSourceName }) @@ -370,21 +366,15 @@ export class TargetsPage extends BaseResourcePage { ).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 { + if (!addBrokeredCredentialsButtonIsVisible) { await this.page.getByText('Manage').click(); - await this.page - .getByRole('link', { name: 'Add Brokered Credentials' }) - .click(); } + await this.page + .getByRole('link', { name: 'Add Brokered Credentials', exact: true }) + .click(); await this.page .getByRole('cell', { name: credentialName }) @@ -427,27 +417,21 @@ export class TargetsPage extends BaseResourcePage { ).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 { + if (!addInjectedCredentialsButtonIsVisible) { await this.page.getByText('Manage').click(); - await this.page - .getByRole('link', { name: 'Add Injected Application' }) - .click(); } + await this.page + .getByRole('link', { + name: 'Add Injected Application Credentials', + exact: true, + }) + .click(); await this.page .getByRole('cell', { name: credentialName }) diff --git a/e2e-tests/admin/pages/users.js b/e2e-tests/admin/pages/users.js index f833e0d1a6..60ee8406b0 100644 --- a/e2e-tests/admin/pages/users.js +++ b/e2e-tests/admin/pages/users.js @@ -43,7 +43,6 @@ export class UsersPage extends BaseResourcePage { .getByRole('link', { name: 'Accounts', exact: true }) .click(); await this.page - .getByRole('article') .getByRole('link', { name: 'Add Accounts', exact: true }) .click(); await this.page diff --git a/e2e-tests/package.json b/e2e-tests/package.json index 21c64b4420..7f00135ee3 100644 --- a/e2e-tests/package.json +++ b/e2e-tests/package.json @@ -28,7 +28,7 @@ "devDependencies": { "@babel/core": "^7.25.2", "@babel/eslint-parser": "^7.21.3", - "@playwright/test": "^1.43.0", + "@playwright/test": "^1.49.0", "concurrently": "^9.1.0", "dotenv": "^16.4.5", "eslint": "^8.54.0", diff --git a/ui/admin/package.json b/ui/admin/package.json index 5b81701671..f886ddf239 100644 --- a/ui/admin/package.json +++ b/ui/admin/package.json @@ -53,7 +53,6 @@ "@glimmer/component": "^1.1.2", "@glimmer/tracking": "^1.1.2", "@hashicorp/ember-asciinema-player": "https://github.com/hashicorp/ember-asciinema-player.git#e047a096039cff70234c232efe75dcad74c6358a", - "@playwright/test": "^1.43.0", "babel-loader": "^9.2.1", "broccoli-asset-rev": "^3.0.0", "concurrently": "^9.1.0", diff --git a/ui/desktop/package.json b/ui/desktop/package.json index 505c849292..b6ffd1e9c0 100644 --- a/ui/desktop/package.json +++ b/ui/desktop/package.json @@ -60,7 +60,6 @@ "@faker-js/faker": "^8.0.2", "@glimmer/component": "^1.1.2", "@glimmer/tracking": "^1.1.2", - "@playwright/test": "^1.43.0", "babel-loader": "^9.2.1", "broccoli-asset-rev": "^3.0.0", "concurrently": "^9.1.0", @@ -99,7 +98,6 @@ "eslint-plugin-qunit": "^8.0.1", "js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1", "loader.js": "^4.7.0", - "playwright": "^1.43.0", "prettier": "^3.0.0", "qunit": "^2.22.0", "qunit-dom": "^3.2.1", diff --git a/yarn.lock b/yarn.lock index 81915039b5..4e1076a9f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4735,12 +4735,12 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@playwright/test@^1.43.0": - version "1.43.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.43.1.tgz#16728a59eb8ce0f60472f98d8886d6cab0fa3e42" - integrity sha512-HgtQzFgNEEo4TE22K/X7sYTYNqEMMTZmFS8kTq6m8hXj+m1D8TgwgIbumHddJa9h4yl4GkKb8/bgAl2+g7eDgA== +"@playwright/test@^1.49.0": + version "1.49.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.49.0.tgz#74227385b58317ee076b86b56d0e1e1b25cff01e" + integrity sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw== dependencies: - playwright "1.43.1" + playwright "1.49.0" "@pnpm/constants@7.1.1": version "7.1.1" @@ -14919,17 +14919,17 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -playwright-core@1.43.1: - version "1.43.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.43.1.tgz#0eafef9994c69c02a1a3825a4343e56c99c03b02" - integrity sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg== +playwright-core@1.49.0: + version "1.49.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.49.0.tgz#8e69ffed3f41855b854982f3632f2922c890afcb" + integrity sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA== -playwright@1.43.1, playwright@^1.43.0: - version "1.43.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.43.1.tgz#8ad08984ac66c9ef3d0db035be54dd7ec9f1c7d9" - integrity sha512-V7SoH0ai2kNt1Md9E3Gwas5B9m8KR2GVvwZnAI6Pg0m3sh7UvgiYhRrhsziCmqMJNouPckiOhk8T+9bSAK0VIA== +playwright@1.49.0: + version "1.49.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.49.0.tgz#df6b9e05423377a99658202844a294a8afb95d0a" + integrity sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A== dependencies: - playwright-core "1.43.1" + playwright-core "1.49.0" optionalDependencies: fsevents "2.3.2"