Icu 14778 add more sessions tests (#2615)
* 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
This commit is contained in:
1 parent
078e36f9b8
commit
e16b4e68c8
7 files changed
+301
-28
No files matched your search
@@ -2,3 +2,5 @@
|
||||
admin/artifacts
|
||||
desktop/artifacts
|
||||
playwright-report
|
||||
|
||||
.auth
|
||||
@@ -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
|
||||
|
||||
@@ -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}`,
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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();
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"prettier": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18.* || 20.*"
|
||||
"node": "20.* || 22.*"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": "eslint --fix",
|
||||
|
||||
Reference in new issue
Block a user