Icu 11916 add flyout for session in prgoress field (#2042)
* feat: 🎸 sessions flyout for targets list view ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-11916 --------- Co-authored-by: Zhihe Li <zhihe.li@hashicorp.com>
This commit is contained in:
1 parent
1122735b87
commit
c06d18940d
8 files changed
+294
-65
No files matched your search
@@ -68,8 +68,8 @@ grant_scope:
|
||||
cluster-url:
|
||||
label: Cluster URL
|
||||
description: The Boundary URL to which to connect (e.g. protocol://host:port)
|
||||
proxy:
|
||||
label: Proxy
|
||||
proxy_url:
|
||||
label: Proxy URL
|
||||
issuer:
|
||||
label: Issuer
|
||||
help: The issuer URL. Boundary expects only the schema, host, and port and will strip off ".well-known/openid-configuration" if present. This will be used for configuration discovery as well as for validation of the "iss" claim.
|
||||
|
||||
@@ -570,6 +570,7 @@ target:
|
||||
titles:
|
||||
new: New Target
|
||||
active-sessions: Active sessions
|
||||
sessions-flyout: Active sessions for {targetDisplayName}
|
||||
actions:
|
||||
create: New Target
|
||||
delete: Delete Target
|
||||
|
||||
@@ -9,6 +9,8 @@ import { action } from '@ember/object';
|
||||
import { loading } from 'ember-loading';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { debounce } from 'core/decorators/debounce';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
|
||||
export default class ScopesScopeProjectsTargetsIndexController extends Controller {
|
||||
// =services
|
||||
@@ -18,6 +20,7 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle
|
||||
@service router;
|
||||
@service session;
|
||||
@service store;
|
||||
@service can;
|
||||
|
||||
// =attributes
|
||||
|
||||
@@ -27,7 +30,7 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle
|
||||
@tracked scopes = [];
|
||||
@tracked page = 1;
|
||||
@tracked pageSize = 10;
|
||||
@tracked sessionsFlyoutActive = false;
|
||||
@tracked selectedTarget;
|
||||
|
||||
// =methods
|
||||
|
||||
@@ -61,6 +64,17 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns active and pending sessions associated with a target.
|
||||
* @returns {object}
|
||||
*/
|
||||
get availableSessions() {
|
||||
const sessions = this.selectedTarget.sessions.filter(
|
||||
(session) => session.isAvailable,
|
||||
);
|
||||
return orderBy(sessions, 'created_time', 'desc');
|
||||
}
|
||||
|
||||
/**
|
||||
* Quick connect method used to call main connect method and handle
|
||||
* connection errors unique to this route
|
||||
@@ -160,11 +174,6 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle
|
||||
this.page = 1;
|
||||
}
|
||||
|
||||
@action
|
||||
toggleSessionsFlyout() {
|
||||
this.sessionsFlyoutActive = !this.sessionsFlyoutActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the scopes query param to value of selectedScopes
|
||||
* to trigger a query and closes the dropdown
|
||||
@@ -174,4 +183,35 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle
|
||||
applyFilter(selectedItems) {
|
||||
this.scopes = [...selectedItems];
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the sessions flyout and initialize variable to store selected target
|
||||
* @param {object} selectedTarget
|
||||
*/
|
||||
@action
|
||||
selectTarget(selectedTarget) {
|
||||
this.selectedTarget = selectedTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the specified session and notifies user of success or error.
|
||||
* @param {SessionModel}
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@notifyError(({ message }) => message, { catch: true })
|
||||
@notifySuccess('notifications.canceled-success')
|
||||
async cancelSession(session) {
|
||||
let updatedSession = session;
|
||||
// fetch session from API to verify we have most up to date record
|
||||
if (this.can.can('read session', session)) {
|
||||
updatedSession = await this.store.findRecord('session', session.id, {
|
||||
reload: true,
|
||||
});
|
||||
}
|
||||
|
||||
await updatedSession.cancelSession();
|
||||
await this.ipc.invoke('stop', { session_id: session.id });
|
||||
this.router.refresh();
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
import Route from '@ember/routing/route';
|
||||
import { inject as service } from '@ember/service';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import {
|
||||
STATUS_SESSION_ACTIVE,
|
||||
STATUS_SESSION_PENDING,
|
||||
@@ -81,23 +80,20 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route {
|
||||
this.can.can('connect target', target),
|
||||
);
|
||||
|
||||
// Retrieve all the 'active' and 'pending' sessions associated with each target.
|
||||
let targetsWithSessions = [];
|
||||
for (const target of targets) {
|
||||
let sessions = await this.store.query('session', {
|
||||
query: {
|
||||
filters: {
|
||||
target_id: { equals: target.id },
|
||||
status: [
|
||||
{ equals: STATUS_SESSION_ACTIVE },
|
||||
{ equals: STATUS_SESSION_PENDING },
|
||||
],
|
||||
},
|
||||
// Retrieve all sessions so that the session and activeSessions getters
|
||||
// in the target model always retrieve the most up-to-date sessions.
|
||||
await this.store.query('session', {
|
||||
query: {
|
||||
filters: {
|
||||
user_id: { equals: this.session.data.authenticated.user_id },
|
||||
status: [
|
||||
{ equals: STATUS_SESSION_ACTIVE },
|
||||
{ equals: STATUS_SESSION_PENDING },
|
||||
],
|
||||
},
|
||||
});
|
||||
sessions = orderBy(sessions, 'created_time', 'desc');
|
||||
targetsWithSessions.push({ target, sessions });
|
||||
}
|
||||
},
|
||||
force_refresh: true,
|
||||
});
|
||||
|
||||
// Query all targets for defining filtering values if entering route for first time
|
||||
if (from !== 'scopes.scope.projects.targets.index') {
|
||||
@@ -111,7 +107,7 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route {
|
||||
}
|
||||
|
||||
return {
|
||||
targets: targetsWithSessions,
|
||||
targets,
|
||||
projects,
|
||||
allTargets: this.allTargets,
|
||||
totalItems,
|
||||
|
||||
@@ -703,3 +703,11 @@ $chevron-down: url("data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%231
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
// Flyout with table
|
||||
.flyout-with-table {
|
||||
.copyable-content a,
|
||||
button {
|
||||
color: var(--token-color-foreground-primary);
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@
|
||||
@columns={{array
|
||||
(hash label=(t 'form.id.label'))
|
||||
(hash label=(t 'resources.target.title'))
|
||||
(hash label=(t 'form.proxy.label'))
|
||||
(hash label=(t 'form.proxy_url.label'))
|
||||
(hash label=(t 'form.started.label'))
|
||||
(hash label=(t 'form.status.label'))
|
||||
(hash label='')
|
||||
|
||||
@@ -80,59 +80,58 @@
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<span class='hds-font-weight-semibold'>
|
||||
{{#if (can 'read model' B.data.target)}}
|
||||
{{#if (can 'read model' B.data)}}
|
||||
<LinkTo
|
||||
data-test-visit-target={{B.data.target.id}}
|
||||
data-test-visit-target={{B.data.id}}
|
||||
@route='scopes.scope.projects.targets.target'
|
||||
@model={{B.data.target.id}}
|
||||
@model={{B.data.id}}
|
||||
@query={{hash isConnecting=false}}
|
||||
>
|
||||
{{B.data.target.displayName}}
|
||||
{{B.data.displayName}}
|
||||
</LinkTo>
|
||||
{{else}}
|
||||
{{B.data.target.displayName}}
|
||||
{{B.data.displayName}}
|
||||
{{/if}}
|
||||
{{#if B.data.target.description}}
|
||||
<p>{{B.data.target.description}}</p>
|
||||
{{#if B.data.description}}
|
||||
<p>{{B.data.description}}</p>
|
||||
{{/if}}
|
||||
</span>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<Copyable
|
||||
@text={{B.data.target.id}}
|
||||
@text={{B.data.id}}
|
||||
@buttonText={{t 'actions.copy-to-clipboard'}}
|
||||
@acknowledgeText={{t 'states.copied'}}
|
||||
>
|
||||
<code>{{B.data.target.id}}</code>
|
||||
<Hds::Text::Code>{{B.data.id}}</Hds::Text::Code>
|
||||
</Copyable>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if B.data.sessions.length}}
|
||||
{{#if B.data.isActive}}
|
||||
<Hds::Button
|
||||
data-test-targets-sessions-flyout-button={{B.data.id}}
|
||||
@text={{t 'actions.yes'}}
|
||||
@color='tertiary'
|
||||
@icon='info'
|
||||
{{on 'click' this.toggleSessionsFlyout}}
|
||||
{{on 'click' (fn this.selectTarget B.data)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if B.data.target.type}}
|
||||
{{#if B.data.type}}
|
||||
<Hds::Badge
|
||||
@text={{t
|
||||
(concat 'resources.target.types.' B.data.target.type)
|
||||
}}
|
||||
@text={{t (concat 'resources.target.types.' B.data.type)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<div>
|
||||
<Copyable
|
||||
@text={{B.data.target.project.id}}
|
||||
@text={{B.data.project.id}}
|
||||
@buttonText={{t 'actions.copy-to-clipboard'}}
|
||||
@acknowledgeText={{t 'states.copied'}}
|
||||
>
|
||||
<code>{{B.data.target.project.id}}</code>
|
||||
<Hds::Text::Code>{{B.data.project.id}}</Hds::Text::Code>
|
||||
</Copyable>
|
||||
</div>
|
||||
{{#if B.data.project.name}}
|
||||
@@ -140,22 +139,22 @@
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if (can 'connect target' B.data.target)}}
|
||||
{{#if (can 'read target' B.data.target)}}
|
||||
{{#if (can 'connect target' B.data)}}
|
||||
{{#if (can 'read target' B.data)}}
|
||||
<Hds::Button
|
||||
data-test-targets-connect-button={{B.data.target.id}}
|
||||
data-test-targets-connect-button={{B.data.id}}
|
||||
@text={{t 'resources.session.actions.connect'}}
|
||||
@color='secondary'
|
||||
@route='scopes.scope.projects.targets.target'
|
||||
@model={{B.data.target.id}}
|
||||
@model={{B.data.id}}
|
||||
@query={{hash isConnecting=true}}
|
||||
/>
|
||||
{{else}}
|
||||
<Hds::Button
|
||||
data-test-targets-connect-button={{B.data.target.id}}
|
||||
data-test-targets-connect-button={{B.data.id}}
|
||||
@text={{t 'resources.session.actions.connect'}}
|
||||
@color='secondary'
|
||||
{{on 'click' (fn this.quickConnect B.data.target)}}
|
||||
{{on 'click' (fn this.quickConnect B.data)}}
|
||||
/>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
@@ -190,6 +189,73 @@
|
||||
<A.Body @text={{t 'resources.target.messages.none.description'}} />
|
||||
</Hds::ApplicationState>
|
||||
{{/if}}
|
||||
{{#if (and this.selectedTarget this.selectedTarget.isActive)}}
|
||||
<Hds::Flyout
|
||||
@onClose={{fn this.selectTarget null}}
|
||||
class='flyout-with-table'
|
||||
data-test-targets-sessions-flyout
|
||||
as |M|
|
||||
>
|
||||
<M.Header>
|
||||
{{t
|
||||
'resources.target.titles.sessions-flyout'
|
||||
targetDisplayName=this.selectedTarget.displayName
|
||||
}}
|
||||
</M.Header>
|
||||
<M.Body>
|
||||
<Hds::Table
|
||||
@model={{this.availableSessions}}
|
||||
@columns={{array
|
||||
(hash label=(t 'form.id.label'))
|
||||
(hash label=(t 'form.started.label'))
|
||||
(hash label=(t 'form.proxy_url.label'))
|
||||
(hash label='')
|
||||
}}
|
||||
@valign='middle'
|
||||
>
|
||||
<:body as |B|>
|
||||
<B.Tr>
|
||||
<B.Td>
|
||||
<Copyable
|
||||
@text={{B.data.id}}
|
||||
@buttonText={{t 'actions.copy-to-clipboard'}}
|
||||
@acknowledgeText={{t 'states.copied'}}
|
||||
>
|
||||
<LinkTo
|
||||
@route='scopes.scope.projects.sessions.session.index'
|
||||
@model={{B.data.id}}
|
||||
data-test-targets-session-detail-link={{B.data.id}}
|
||||
>
|
||||
<Hds::Text::Code>{{B.data.id}}</Hds::Text::Code>
|
||||
</LinkTo>
|
||||
</Copyable>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
<time datetime={{format-date-iso B.data.created_time}}>
|
||||
{{format-date-iso-human B.data.created_time}}
|
||||
</time>
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{B.data.proxy}}
|
||||
</B.Td>
|
||||
<B.Td>
|
||||
{{#if (can 'cancel session' B.data)}}
|
||||
<Hds::Button
|
||||
data-test-session-flyout-cancel-button={{B.data.id}}
|
||||
@text={{t 'resources.session.actions.end'}}
|
||||
@icon='x'
|
||||
@isIconOnly={{true}}
|
||||
@color='secondary'
|
||||
{{on 'click' (fn this.cancelSession B.data)}}
|
||||
/>
|
||||
{{/if}}
|
||||
</B.Td>
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
</M.Body>
|
||||
</Hds::Flyout>
|
||||
{{/if}}
|
||||
</page.body>
|
||||
|
||||
</Rose::Layout::Page>
|
||||
@@ -37,6 +37,11 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
const ROSE_DIALOG_RETRY_BUTTON = '.rose-dialog footer .rose-button-primary';
|
||||
const ROSE_DIALOG_CANCEL_BUTTON =
|
||||
'.rose-dialog footer .rose-button-secondary';
|
||||
const SESSIONS_FLYOUT = '[data-test-targets-sessions-flyout]';
|
||||
const SESSIONS_FLYOUT_TITLE =
|
||||
'[data-test-targets-sessions-flyout] .hds-flyout__title';
|
||||
const SESSIONS_FLYOUT_CLOSE_BUTTON =
|
||||
'[data-test-targets-sessions-flyout] .hds-flyout__dismiss';
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
@@ -65,7 +70,7 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
projects: null,
|
||||
targets: null,
|
||||
target: null,
|
||||
sessions: null,
|
||||
session: null,
|
||||
};
|
||||
|
||||
const setDefaultClusterUrl = (test) => {
|
||||
@@ -110,6 +115,7 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
{
|
||||
scope: instances.scopes.project,
|
||||
status: 'active',
|
||||
target_id: instances.target.id,
|
||||
},
|
||||
'withAssociations',
|
||||
);
|
||||
@@ -122,6 +128,7 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
urls.projects = `${urls.scopes.org}/projects`;
|
||||
urls.targets = `${urls.projects}/targets`;
|
||||
urls.target = `${urls.targets}/${instances.target.id}`;
|
||||
urls.session = `${urls.projects}/sessions/${instances.session.id}`;
|
||||
|
||||
// Generate resource counter
|
||||
getTargetCount = () => this.server.schema.targets.all().models.length;
|
||||
@@ -131,12 +138,11 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
setDefaultClusterUrl(this);
|
||||
|
||||
this.ipcStub.withArgs('isClientDaemonRunning').returns(true);
|
||||
this.stubClientDaemonSearch('targets', 'targets', 'sessions');
|
||||
this.stubClientDaemonSearch('targets', 'sessions', 'targets');
|
||||
});
|
||||
|
||||
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
|
||||
invalidateSession();
|
||||
assert.expect(2);
|
||||
await visit(urls.targets);
|
||||
await a11yAudit();
|
||||
|
||||
@@ -145,7 +151,6 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
});
|
||||
|
||||
test('visiting targets index', async function (assert) {
|
||||
assert.expect(2);
|
||||
const targetsCount = getTargetCount();
|
||||
await visit(urls.projects);
|
||||
|
||||
@@ -157,7 +162,6 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
});
|
||||
|
||||
test('visiting a target', async function (assert) {
|
||||
assert.expect(2);
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
@@ -170,7 +174,6 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
});
|
||||
|
||||
test('visiting targets list view with no targets', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.db.targets.remove();
|
||||
this.stubClientDaemonSearch('targets', 'targets');
|
||||
|
||||
@@ -182,7 +185,6 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
});
|
||||
|
||||
test('user cannot navigate to a target without proper authorization', async function (assert) {
|
||||
assert.expect(1);
|
||||
instances.target.authorized_actions =
|
||||
instances.target.authorized_actions.filter((item) => item !== 'read');
|
||||
this.stubClientDaemonSearch('targets', 'targets');
|
||||
@@ -197,7 +199,6 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
});
|
||||
|
||||
test('user can connect to a target with proper authorization', async function (assert) {
|
||||
assert.expect(2);
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
@@ -211,7 +212,6 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
});
|
||||
|
||||
test('user cannot connect to a target without proper authorization', async function (assert) {
|
||||
assert.expect(2);
|
||||
instances.target.authorized_actions =
|
||||
instances.target.authorized_actions.filter(
|
||||
(item) => item !== 'authorize-session',
|
||||
@@ -231,13 +231,13 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
});
|
||||
|
||||
test('user is redirected to target details page when unable to connect from list view if they have read and authorize-session permissions', async function (assert) {
|
||||
assert.expect(3);
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').rejects();
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
await visit(urls.projects);
|
||||
|
||||
await visit(urls.targets);
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
|
||||
await click(`[data-test-targets-connect-button="${instances.target.id}"]`);
|
||||
|
||||
@@ -247,7 +247,6 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
});
|
||||
|
||||
test('user can connect without target read permissions', async function (assert) {
|
||||
assert.expect(2);
|
||||
instances.target.authorized_actions =
|
||||
instances.target.authorized_actions.filter((item) => item !== 'read');
|
||||
this.stubClientDaemonSearch('targets');
|
||||
@@ -258,8 +257,9 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
port: 'p_123',
|
||||
protocol: 'tcp',
|
||||
});
|
||||
await visit(urls.projects);
|
||||
|
||||
await visit(urls.targets);
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
|
||||
await click(`[data-test-targets-connect-button="${instances.target.id}"]`);
|
||||
|
||||
@@ -271,7 +271,6 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
});
|
||||
|
||||
test('user can retry connect without target read permissions', async function (assert) {
|
||||
assert.expect(5);
|
||||
instances.target.authorized_actions =
|
||||
instances.target.authorized_actions.filter((item) => item !== 'read');
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
@@ -279,8 +278,9 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
this.stubClientDaemonSearch('targets', 'targets');
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
await visit(urls.projects);
|
||||
|
||||
await visit(urls.targets);
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
|
||||
await click(`[data-test-targets-connect-button="${instances.target.id}"]`);
|
||||
|
||||
@@ -290,4 +290,122 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
assert.dom(ROSE_DIALOG_RETRY_BUTTON).hasText('Retry');
|
||||
assert.dom(ROSE_DIALOG_CANCEL_BUTTON).hasText('Cancel');
|
||||
});
|
||||
|
||||
test('user can open sessions flyout when target has active or pending sessions', async function (assert) {
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
|
||||
assert
|
||||
.dom(
|
||||
`[data-test-targets-sessions-flyout-button="${instances.target.id}"]`,
|
||||
)
|
||||
.exists();
|
||||
|
||||
await click(
|
||||
`[data-test-targets-sessions-flyout-button="${instances.target.id}"]`,
|
||||
);
|
||||
|
||||
assert.dom(SESSIONS_FLYOUT).exists();
|
||||
assert.dom(SESSIONS_FLYOUT_TITLE).includesText(`Active sessions for`);
|
||||
|
||||
await click(SESSIONS_FLYOUT_CLOSE_BUTTON);
|
||||
|
||||
assert.dom(SESSIONS_FLYOUT).doesNotExist();
|
||||
});
|
||||
|
||||
test('user can cancel a session from inside target sessions flyout', async function (assert) {
|
||||
this.stubClientDaemonSearch(
|
||||
'targets',
|
||||
'sessions',
|
||||
'targets',
|
||||
'targets',
|
||||
'',
|
||||
'targets',
|
||||
);
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
await click(
|
||||
`[data-test-targets-sessions-flyout-button="${instances.target.id}"]`,
|
||||
);
|
||||
|
||||
assert.dom(SESSIONS_FLYOUT).exists();
|
||||
assert
|
||||
.dom(`[data-test-session-flyout-cancel-button="${instances.session.id}"]`)
|
||||
.exists();
|
||||
|
||||
await click(
|
||||
`[data-test-session-flyout-cancel-button="${instances.session.id}"]`,
|
||||
);
|
||||
|
||||
assert.dom(SESSIONS_FLYOUT).doesNotExist();
|
||||
assert
|
||||
.dom(
|
||||
`[data-test-targets-sessions-flyout-button="${instances.target.id}"]`,
|
||||
)
|
||||
.doesNotExist();
|
||||
assert.strictEqual(currentURL(), urls.targets);
|
||||
});
|
||||
|
||||
test('user cannot cancel a session from inside target sessions flyout without permissions', async function (assert) {
|
||||
instances.session.authorized_actions =
|
||||
instances.session.authorized_actions.filter((item) => item !== 'cancel');
|
||||
this.stubClientDaemonSearch('targets', 'sessions', 'targets');
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
await click(
|
||||
`[data-test-targets-sessions-flyout-button="${instances.target.id}"]`,
|
||||
);
|
||||
|
||||
assert.dom(SESSIONS_FLYOUT).exists();
|
||||
assert.strictEqual(currentURL(), urls.targets);
|
||||
assert
|
||||
.dom(`[data-test-session-flyout-cancel-button="${instances.target.id}"]`)
|
||||
.doesNotExist();
|
||||
assert
|
||||
.dom(`[data-test-targets-session-detail-link="${instances.session.id}"]`)
|
||||
.exists();
|
||||
});
|
||||
|
||||
test('user can navigate to session details from sessions table in flyout', async function (assert) {
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
await click(
|
||||
`[data-test-targets-sessions-flyout-button="${instances.target.id}"]`,
|
||||
);
|
||||
|
||||
assert.dom(SESSIONS_FLYOUT).exists();
|
||||
|
||||
await click(
|
||||
`[data-test-targets-session-detail-link="${instances.session.id}"]`,
|
||||
);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.session);
|
||||
});
|
||||
|
||||
test('user can navigate to session details from sessions table in flyout without permissions', async function (assert) {
|
||||
instances.session.authorized_actions =
|
||||
instances.session.authorized_actions.filter((item) => item !== 'read');
|
||||
this.stubClientDaemonSearch('targets', 'sessions', 'targets');
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
await click(
|
||||
`[data-test-targets-sessions-flyout-button="${instances.target.id}"]`,
|
||||
);
|
||||
|
||||
assert.dom(SESSIONS_FLYOUT).exists();
|
||||
assert
|
||||
.dom(`[data-test-targets-session-detail-link="${instances.session.id}"]`)
|
||||
.exists();
|
||||
|
||||
await click(
|
||||
`[data-test-targets-session-detail-link="${instances.session.id}"]`,
|
||||
);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.session);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user