Icu 16439 enable session recording create storage bucket & index (#2886)

* test: 💍 update selectors for create-storage-bucket-test

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-16439

* test: 💍 add selectors for enable session recording index test

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-16439

* fix: 🐛 clean up duplicate test selectors

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-16439

* refactor: 💡 move single file use selectors back to file

✅ Closes: https://hashicorp.atlassian.net/browse/ICU-16439
This commit is contained in:
Cameron Perera authored and GitHub committed 2025-07-07 11:47:04 -05:00
1 parent 44624e3820
commit f0feb9e563
5 files changed
+134 -119

No files matched your search

@@ -26,7 +26,7 @@
{{#if @model.errors.name}}
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<E.Message>{{error.message}}</E.Message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
@@ -26,7 +26,7 @@
{{#if @model.errors.name}}
<F.Error as |E|>
{{#each @model.errors.name as |error|}}
<E.Message>{{error.message}}</E.Message>
<E.Message data-test-error-message-name>{{error.message}}</E.Message>
{{/each}}
</F.Error>
{{/if}}
@@ -21,16 +21,11 @@ module(
let features;
let getStorageBucketCount;
const SAVE_BTN_SELECTOR = '[type="submit"]';
const CANCEL_BTN_SELECTOR = '.rose-form-actions [type="button"]';
const NAME_FIELD_SELECTOR = '[name="name"]';
const FIELD_ERROR_TEXT_SELECTOR = '.hds-form-error__message';
const NAME_FIELD_TEXT = 'random string';
const BUCKET_NAME_FIELD_SELECTOR = '[name="bucket_name"]';
const BUCKET_PREFIX_FIELD_SELECTOR = '[name="bucket_prefix"]';
const EDITOR_WORKER_FILTER =
'[data-test-code-editor-field-editor] textarea';
const EDITOR_WORKER_FILTER_VALUE = '"dev" in "/tags/env"';
const FIELD_BUCKET_NAME = '[name="bucket_name"]';
const FIELD_BUCKET_PREFIX = '[name="bucket_prefix"]';
const FIELD_EDITOR = '[data-test-code-editor-field-editor] textarea';
const WORKER_FILTER_VALUE = '"dev" in "/tags/env"';
const FIELD_SCOPE = (scope) => `[value="${scope}"]`;
const instances = {
scopes: {
@@ -40,8 +35,6 @@ module(
};
const urls = {
globalScope: null,
projectScope: null,
targets: null,
target: null,
enableSessionRecording: null,
@@ -63,10 +56,8 @@ module(
scope: instances.scopes.project,
type: TYPE_TARGET_SSH,
});
urls.globalScope = `/scopes/global`;
urls.projectScope = `/scopes/${instances.scopes.project.id}`;
urls.targets = `${urls.projectScope}/targets`;
urls.targets = `/scopes/${instances.scopes.project.id}/targets`;
urls.target = `${urls.targets}/${instances.target.id}`;
urls.enableSessionRecording = `${urls.target}/enable-session-recording`;
urls.newStorageBucket = `${urls.enableSessionRecording}/create-storage-bucket`;
@@ -82,22 +73,25 @@ module(
const storageBucketCount = getStorageBucketCount();
await visit(urls.enableSessionRecording);
await click(`[href="${urls.newStorageBucket}"]`);
await fillIn(NAME_FIELD_SELECTOR, NAME_FIELD_TEXT);
await click('[value="global"]');
await fillIn(EDITOR_WORKER_FILTER, EDITOR_WORKER_FILTER_VALUE);
await click(commonSelectors.HREF(urls.newStorageBucket));
await fillIn(
commonSelectors.FIELD_NAME,
commonSelectors.FIELD_NAME_VALUE,
);
await click(FIELD_SCOPE('global'));
await fillIn(FIELD_EDITOR, WORKER_FILTER_VALUE);
assert.dom(BUCKET_NAME_FIELD_SELECTOR).isNotDisabled();
assert.dom(BUCKET_PREFIX_FIELD_SELECTOR).isNotDisabled();
assert.dom(BUCKET_NAME_FIELD_SELECTOR).doesNotHaveAttribute('readOnly');
assert.dom(BUCKET_PREFIX_FIELD_SELECTOR).doesNotHaveAttribute('readOnly');
assert.dom(FIELD_BUCKET_NAME).isNotDisabled();
assert.dom(FIELD_BUCKET_PREFIX).isNotDisabled();
assert.dom(FIELD_BUCKET_NAME).doesNotHaveAttribute('readOnly');
assert.dom(FIELD_BUCKET_PREFIX).doesNotHaveAttribute('readOnly');
await click(SAVE_BTN_SELECTOR);
await click(commonSelectors.SAVE_BTN);
const storageBucket = this.server.schema.storageBuckets.findBy({
name: NAME_FIELD_TEXT,
name: commonSelectors.FIELD_NAME_VALUE,
});
assert.strictEqual(storageBucket.name, NAME_FIELD_TEXT);
assert.strictEqual(storageBucket.name, commonSelectors.FIELD_NAME_VALUE);
assert.strictEqual(storageBucket.scopeId, 'global');
assert.strictEqual(getStorageBucketCount(), storageBucketCount + 1);
});
@@ -106,22 +100,25 @@ module(
const storageBucketCount = getStorageBucketCount();
await visit(urls.enableSessionRecording);
await click(`[href="${urls.newStorageBucket}"]`);
await fillIn(NAME_FIELD_SELECTOR, NAME_FIELD_TEXT);
await click(`[value="${instances.scopes.org.scope.id}"]`);
await fillIn(EDITOR_WORKER_FILTER, EDITOR_WORKER_FILTER_VALUE);
await click(commonSelectors.HREF(urls.newStorageBucket));
await fillIn(
commonSelectors.FIELD_NAME,
commonSelectors.FIELD_NAME_VALUE,
);
await click(FIELD_SCOPE(instances.scopes.org.scope.id));
await fillIn(FIELD_EDITOR, WORKER_FILTER_VALUE);
assert.dom(BUCKET_NAME_FIELD_SELECTOR).isNotDisabled();
assert.dom(BUCKET_PREFIX_FIELD_SELECTOR).isNotDisabled();
assert.dom(BUCKET_NAME_FIELD_SELECTOR).doesNotHaveAttribute('readOnly');
assert.dom(BUCKET_PREFIX_FIELD_SELECTOR).doesNotHaveAttribute('readOnly');
assert.dom(FIELD_BUCKET_NAME).isNotDisabled();
assert.dom(FIELD_BUCKET_PREFIX).isNotDisabled();
assert.dom(FIELD_BUCKET_NAME).doesNotHaveAttribute('readOnly');
assert.dom(FIELD_BUCKET_PREFIX).doesNotHaveAttribute('readOnly');
await click(SAVE_BTN_SELECTOR);
await click(commonSelectors.SAVE_BTN);
const storageBucket = this.server.schema.storageBuckets.findBy({
name: NAME_FIELD_TEXT,
name: commonSelectors.FIELD_NAME_VALUE,
});
assert.strictEqual(storageBucket.name, NAME_FIELD_TEXT);
assert.strictEqual(storageBucket.name, commonSelectors.FIELD_NAME_VALUE);
assert.strictEqual(storageBucket.scopeId, instances.scopes.org.scope.id);
assert.strictEqual(getStorageBucketCount(), storageBucketCount + 1);
});
@@ -130,15 +127,20 @@ module(
const storageBucketCount = getStorageBucketCount();
await visit(urls.enableSessionRecording);
await click(`[href="${urls.newStorageBucket}"]`);
await fillIn(NAME_FIELD_SELECTOR, NAME_FIELD_TEXT);
await click(CANCEL_BTN_SELECTOR);
await click(commonSelectors.HREF(urls.newStorageBucket));
await fillIn(
commonSelectors.FIELD_NAME,
commonSelectors.FIELD_NAME_VALUE,
);
await click(commonSelectors.CANCEL_BTN);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
assert.strictEqual(getStorageBucketCount(), storageBucketCount);
});
test('saving a new storage bucket with invalid fields displays error messages', async function (assert) {
const errorMessage = 'The request was invalid.';
const errorDescription = 'Name is required.';
this.server.post('/storage-buckets', () => {
return new Response(
400,
@@ -146,12 +148,12 @@ module(
{
status: 400,
code: 'invalid_argument',
message: 'The request was invalid.',
message: errorMessage,
details: {
request_fields: [
{
name: 'name',
description: 'Name is required.',
description: errorDescription,
},
],
},
@@ -160,14 +162,12 @@ module(
});
await visit(urls.enableSessionRecording);
await click(`[href="${urls.newStorageBucket}"]`);
await fillIn(EDITOR_WORKER_FILTER, EDITOR_WORKER_FILTER_VALUE);
await click(SAVE_BTN_SELECTOR);
await click(commonSelectors.HREF(urls.newStorageBucket));
await fillIn(FIELD_EDITOR, WORKER_FILTER_VALUE);
await click(commonSelectors.SAVE_BTN);
assert
.dom(commonSelectors.ALERT_TOAST_BODY)
.hasText('The request was invalid.');
assert.dom(FIELD_ERROR_TEXT_SELECTOR).hasText('Name is required.');
assert.dom(commonSelectors.ALERT_TOAST_BODY).hasText(errorMessage);
assert.dom(commonSelectors.FIELD_NAME_ERROR).hasText(errorDescription);
});
},
);
@@ -11,6 +11,8 @@ import a11yAudit from 'ember-a11y-testing/test-support/audit';
import { authenticateSession } from 'ember-simple-auth/test-support';
import { TYPE_TARGET_SSH } from 'api/models/target';
import select from '@ember/test-helpers/dom/select';
import * as commonSelectors from 'admin/tests/helpers/selectors';
import * as selectors from '../selectors';
module(
'Acceptance | targets | enable session recording | index',
@@ -22,17 +24,13 @@ module(
let storageBucketOne;
let storageBucketTwo;
const SETTINGS_LINK_SELECTOR = '.target-sidebar .hds-link-standalone';
const ENABLE_BUTTON_SELECTOR = '.target-sidebar .hds-button';
const DROPDOWN_SELECTOR = '[name=storage_bucket_id]';
const LINK_LIST_SELECTOR = '.link-list-item > a';
const LINK_LIST_SELECTOR_ITEM_TEXT = '.link-list-item__text';
const BADGE_TEXT_SELECTOR = '.target-sidebar .hds-badge__text';
const TOGGLE_SELECTOR = '[name=target-enable-session-recording]';
const SESSION_RECORDING_SETTINGS_LINK =
'.target-sidebar .hds-link-standalone';
const SESSION_RECORDING_ENABLE_BTN = '.target-sidebar .hds-button';
const ENABLE_TOGGLE = '[name=target-enable-session-recording]';
const BADGE_TEXT = '.target-sidebar .hds-badge__text';
const LINK_TO_NEW_STORAGE_BUCKET =
'.enable-session-recording-toggle .hds-link-standalone';
const SAVE_BTN_SELECTOR = '.rose-form-actions [type="submit"]';
const CANCEL_BTN_SELECTOR = '.rose-form-actions [type="button"]';
// Instances
const instances = {
@@ -45,9 +43,6 @@ module(
//URLs
const urls = {
globalScope: null,
orgScope: null,
projectScope: null,
targets: null,
target: null,
enableSessionRecording: null,
@@ -82,13 +77,10 @@ module(
storageBucketTwo = instances.storageBucket[1];
// Generate route URLs for resources
urls.globalScope = `/scopes/global`;
urls.orgScope = `/scopes/${instances.scopes.org.id}/scopes`;
urls.projectScope = `/scopes/${instances.scopes.project.id}`;
urls.targets = `${urls.projectScope}/targets`;
urls.targets = `/scopes/${instances.scopes.project.id}/targets`;
urls.target = `${urls.targets}/${instances.target.id}`;
urls.enableSessionRecording = `${urls.target}/enable-session-recording`;
urls.storageBuckets = `${urls.globalScope}/storage-buckets`;
urls.storageBuckets = '/scopes/global/storage-buckets';
urls.newStorageBucket = `${urls.enableSessionRecording}/create-storage-bucket`;
urls.storageBucket = `${urls.storageBuckets}/${storageBucketOne.id}`;
@@ -99,16 +91,21 @@ module(
test('cannot enable session recording for a target without proper authorization', async function (assert) {
assert.false(featuresService.isEnabled('ssh-session-recording'));
await visit(urls.target);
await a11yAudit();
assert.dom('.target-sidebar a').doesNotExist();
assert.dom(SESSION_RECORDING_ENABLE_BTN).doesNotExist();
});
test('users can click on enable-recording button in target session-recording sidebar and it takes them to enable session recording', async function (assert) {
featuresService.enable('ssh-session-recording');
await visit(urls.target);
assert.dom(SETTINGS_LINK_SELECTOR).doesNotExist();
await click(ENABLE_BUTTON_SELECTOR);
assert.dom(SESSION_RECORDING_SETTINGS_LINK).doesNotExist();
await click(SESSION_RECORDING_ENABLE_BTN);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
});
@@ -119,7 +116,8 @@ module(
enableSessionRecording: true,
});
await visit(urls.target);
await click(SETTINGS_LINK_SELECTOR);
await click(SESSION_RECORDING_SETTINGS_LINK);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
});
@@ -130,37 +128,45 @@ module(
enableSessionRecording: true,
});
await visit(urls.target);
await click(LINK_LIST_SELECTOR);
await click(selectors.LINK_LIST_ITEM);
assert.strictEqual(currentURL(), urls.storageBucket);
});
test('toggle should be disabled and storage buckets list should be shown when enable session recording button is clicked', async function (assert) {
featuresService.enable('ssh-session-recording');
await visit(urls.target);
await click(ENABLE_BUTTON_SELECTOR);
await click(SESSION_RECORDING_ENABLE_BTN);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
assert.dom(TOGGLE_SELECTOR).isVisible();
assert.dom(DROPDOWN_SELECTOR).isNotVisible();
assert.dom(ENABLE_TOGGLE).isVisible();
assert.dom(selectors.STORAGE_BUCKET_DROPDOWN).isNotVisible();
});
test('storage buckets list is hidden when toggle is disabled', async function (assert) {
featuresService.enable('ssh-session-recording');
await visit(urls.target);
await click(ENABLE_BUTTON_SELECTOR);
await click(SESSION_RECORDING_ENABLE_BTN);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
assert.dom(TOGGLE_SELECTOR).isVisible();
assert.dom(DROPDOWN_SELECTOR).isNotVisible();
await click(TOGGLE_SELECTOR);
assert.dom(DROPDOWN_SELECTOR).isVisible();
assert.dom(ENABLE_TOGGLE).isVisible();
assert.dom(selectors.STORAGE_BUCKET_DROPDOWN).isNotVisible();
await click(ENABLE_TOGGLE);
assert.dom(selectors.STORAGE_BUCKET_DROPDOWN).isVisible();
});
test('link to add new storage bucket should be displayed and redirect to new storage buckets form', async function (assert) {
featuresService.enable('ssh-session-recording');
await visit(urls.target);
await click(ENABLE_BUTTON_SELECTOR);
await click(SESSION_RECORDING_ENABLE_BTN);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
assert.dom(LINK_TO_NEW_STORAGE_BUCKET).isVisible();
await click(LINK_TO_NEW_STORAGE_BUCKET);
assert.strictEqual(currentURL(), urls.newStorageBucket);
});
@@ -171,45 +177,54 @@ module(
enable_session_recording: true,
});
await visit(urls.target);
await click(SETTINGS_LINK_SELECTOR);
await click(SESSION_RECORDING_SETTINGS_LINK);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
const existingStorageBucketId = find(DROPDOWN_SELECTOR).value;
const existingStorageBucketId = find(
selectors.STORAGE_BUCKET_DROPDOWN,
).value;
//disable the toggle, dropdown should be hidden
await click(TOGGLE_SELECTOR);
assert.dom(DROPDOWN_SELECTOR).isNotVisible();
await click(ENABLE_TOGGLE);
assert.dom(selectors.STORAGE_BUCKET_DROPDOWN).isNotVisible();
//enable the toggle now,
//it should have the previously selected storage_bucket_id
await click(TOGGLE_SELECTOR);
assert.dom(DROPDOWN_SELECTOR).hasValue(existingStorageBucketId);
await click(ENABLE_TOGGLE);
assert
.dom(selectors.STORAGE_BUCKET_DROPDOWN)
.hasValue(existingStorageBucketId);
});
test('can assign a storage bucket for the target', async function (assert) {
featuresService.enable('ssh-session-recording');
await visit(urls.target);
await click(ENABLE_BUTTON_SELECTOR);
await click(SESSION_RECORDING_ENABLE_BTN);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
await click(TOGGLE_SELECTOR);
assert.dom(DROPDOWN_SELECTOR).hasNoValue();
await select(DROPDOWN_SELECTOR, storageBucketTwo.id);
assert.dom(DROPDOWN_SELECTOR).hasValue();
await click(SAVE_BTN_SELECTOR);
await click(ENABLE_TOGGLE);
assert.dom(selectors.STORAGE_BUCKET_DROPDOWN).hasNoValue();
await select(selectors.STORAGE_BUCKET_DROPDOWN, storageBucketTwo.id);
assert.dom(selectors.STORAGE_BUCKET_DROPDOWN).hasValue();
await click(commonSelectors.SAVE_BTN);
assert.strictEqual(currentURL(), urls.target);
assert.strictEqual(
find(BADGE_TEXT_SELECTOR).textContent.trim(),
intl.t(
`resources.storage-bucket.plugin-types.${storageBucketTwo.plugin.name}`,
),
);
assert.strictEqual(
find(LINK_LIST_SELECTOR_ITEM_TEXT).textContent.trim(),
storageBucketTwo.name,
);
assert
.dom(BADGE_TEXT)
.hasText(
intl.t(
`resources.storage-bucket.plugin-types.${storageBucketTwo.plugin.name}`,
),
);
assert.dom(selectors.LINK_LIST_ITEM_TEXT).hasText(storageBucketTwo.name);
});
test('can cancel changes to an existing storage bucket selection', async function (assert) {
@@ -220,25 +235,23 @@ module(
});
await visit(urls.target);
await click(SETTINGS_LINK_SELECTOR);
await click(SESSION_RECORDING_SETTINGS_LINK);
assert.strictEqual(currentURL(), urls.enableSessionRecording);
//update the storage bucket selection
await select(DROPDOWN_SELECTOR, storageBucketTwo);
await select(selectors.STORAGE_BUCKET_DROPDOWN, storageBucketTwo);
await click(commonSelectors.CANCEL_BTN);
await click(CANCEL_BTN_SELECTOR);
assert.strictEqual(currentURL(), urls.target);
assert.strictEqual(
find(BADGE_TEXT_SELECTOR).textContent.trim(),
intl.t(
`resources.storage-bucket.plugin-types.${storageBucketOne.plugin.name}`,
),
);
assert.strictEqual(
find(LINK_LIST_SELECTOR_ITEM_TEXT).textContent.trim(),
storageBucketOne.name,
);
assert
.dom(BADGE_TEXT)
.hasText(
intl.t(
`resources.storage-bucket.plugin-types.${storageBucketOne.plugin.name}`,
),
);
assert.dom(selectors.LINK_LIST_ITEM_TEXT).hasText(storageBucketOne.name);
});
},
);
@@ -49,6 +49,8 @@ export const RECORDING_SETTINGS_LINK = '.target-sidebar .hds-link-standalone';
export const LINK_LIST_ITEM = '.link-list-item > a';
export const LINK_LIST_ITEM_TEXT = '.link-list-item__text';
export const STORAGE_BUCKET_DROPDOWN = '[name=storage_bucket_id]';
export const NO_RESULTS_MSG = '[data-test-no-target-results]';
export const TABLE_ACTIVE_SESSIONS = (id) =>
`tbody [data-test-targets-table-row="${id}"] .hds-table__td:nth-child(3) a`;