feat: 🎸 Add section for client agent in settings (#2442)
* feat: 🎸 Add section for client agent in settings ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-14780
This commit is contained in:
1 parent
de5defeab2
commit
edaf19f59b
20 files changed
+403
-80
No files matched your search
@@ -199,18 +199,18 @@ export default function initializeMockIPC(server, config) {
|
||||
getDesktopVersion() {
|
||||
return { desktopVersion: '1.0.0' };
|
||||
}
|
||||
|
||||
getLogLevel() {
|
||||
return 'debug';
|
||||
return 'info';
|
||||
}
|
||||
|
||||
setLogLevel() {
|
||||
return 'warn';
|
||||
}
|
||||
|
||||
getLogPath() {
|
||||
return '~/.config/Boundary/logs/desktop-client.log';
|
||||
}
|
||||
setLogLevel() {}
|
||||
clientAgentStatus() {
|
||||
return { version: '0.0.1-dev', status: 'running' };
|
||||
}
|
||||
pauseClientAgent() {}
|
||||
resumeClientAgent() {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,6 +22,8 @@ back: Back
|
||||
view: View
|
||||
retry: Retry
|
||||
play: Play
|
||||
pause: Pause
|
||||
resume: Resume
|
||||
get-topic-help: Get help for this topic
|
||||
copy-to-clipboard: Copy to Clipboard
|
||||
copy-error-detail-to-clipboard: Copy error detail to clipboard
|
||||
|
||||
@@ -109,7 +109,11 @@ settings:
|
||||
application: Application
|
||||
cli: CLI
|
||||
cache-daemon: Cache Daemon
|
||||
client-agent:
|
||||
title: Boundary Client Agent
|
||||
description: Required to use Transparent Sessions
|
||||
client: Client
|
||||
version: Version
|
||||
color-theme: Color Theme
|
||||
provider: Provider
|
||||
hcp: HashiCorp Cloud Platform
|
||||
@@ -127,3 +131,6 @@ settings:
|
||||
warn: Warn
|
||||
info: Info
|
||||
debug: Debug
|
||||
alerts:
|
||||
cache-daemon: There may be a problem with the cache daemon
|
||||
client-agent: There may be a problem with the client agent
|
||||
@@ -27,5 +27,3 @@ onboarding-failed:
|
||||
required-field: This is a required field.
|
||||
client-agent-failed:
|
||||
sessions: Unable to retrieve sessions from the client agent.
|
||||
settings-cache:
|
||||
title: There may be a problem with the cache daemon
|
||||
@@ -17,6 +17,7 @@ failed: Failed
|
||||
installed: Installed
|
||||
not-running: Not Running
|
||||
running: Running
|
||||
paused: Paused
|
||||
authenticated: Authenticated
|
||||
unauthenticated: Unauthenticated
|
||||
loading:
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
{{t 'settings.client'}}
|
||||
</Hds::Text::Display>
|
||||
<Hds::Text::Body @tag='p' @weight='medium'>
|
||||
{{@model.formattedDesktopVersion}}
|
||||
{{@model.desktopVersion}}
|
||||
</Hds::Text::Body>
|
||||
</div>
|
||||
<div>
|
||||
@@ -18,7 +18,7 @@
|
||||
{{t 'settings.cli'}}
|
||||
</Hds::Text::Display>
|
||||
<Hds::Text::Body @tag='p' @weight='medium'>
|
||||
{{@model.formattedCliVersion}}
|
||||
{{@model.cliVersion}}
|
||||
</Hds::Text::Body>
|
||||
</div>
|
||||
<div>
|
||||
@@ -26,8 +26,8 @@
|
||||
{{t 'settings.cache-daemon'}}
|
||||
</Hds::Text::Display>
|
||||
<Hds::Text::Body @tag='p' @weight='medium' data-test-cache-version>
|
||||
{{#if @model.formattedCacheVersion}}
|
||||
{{@model.formattedCacheVersion}}
|
||||
{{#if @model.cacheDaemonStatus.version}}
|
||||
{{@model.cacheDaemonStatus.version}}
|
||||
<Hds::Badge
|
||||
@text={{t 'states.running'}}
|
||||
@icon='running'
|
||||
@@ -43,11 +43,11 @@
|
||||
|
||||
</Hds::Text::Body>
|
||||
</div>
|
||||
{{#if @model.cacheDaemonErrors}}
|
||||
{{#if @model.cacheDaemonStatus.errors}}
|
||||
<Hds::Alert @type='inline' @color='warning' as |A|>
|
||||
<A.Title>{{t 'errors.settings-cache.title'}}</A.Title>
|
||||
<A.Title>{{t 'settings.alerts.cache-daemon'}}</A.Title>
|
||||
<A.Description>
|
||||
{{#each @model.cacheDaemonErrors as |error|}}
|
||||
{{#each @model.cacheDaemonStatus.errors as |error|}}
|
||||
<Hds::Text::Body @tag='p'>
|
||||
{{#if error.name}}
|
||||
<strong>{{error.name}}: </strong>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
|
||||
{{#if this.isClientAgentActive}}
|
||||
<SettingsCard
|
||||
@header={{t 'settings.client-agent.title'}}
|
||||
@icon='network-alt'
|
||||
@description={{t 'settings.client-agent.description'}}
|
||||
>
|
||||
<:state>
|
||||
{{#if this.isClientAgentStatusRunning}}
|
||||
<Hds::Badge
|
||||
@text={{t 'states.running'}}
|
||||
@icon='running'
|
||||
@color='success'
|
||||
/>
|
||||
{{else}}
|
||||
<Hds::Badge
|
||||
@text={{t 'states.paused'}}
|
||||
@icon='pause-circle'
|
||||
@color='neutral'
|
||||
/>
|
||||
{{/if}}
|
||||
</:state>
|
||||
<:body>
|
||||
<div>
|
||||
<Hds::Text::Display>
|
||||
{{t 'settings.version'}}
|
||||
</Hds::Text::Display>
|
||||
<Hds::Text::Body @tag='p' @weight='medium'>
|
||||
{{@model.clientAgentStatus.version}}
|
||||
</Hds::Text::Body>
|
||||
</div>
|
||||
{{#if @model.clientAgentStatus.errors}}
|
||||
<Hds::Alert @type='inline' @color='warning' as |A|>
|
||||
<A.Title>{{t 'settings.alerts.client-agent'}}</A.Title>
|
||||
<A.Description>
|
||||
{{#each @model.clientAgentStatus.errors as |error|}}
|
||||
<Hds::Text::Body @tag='p'>
|
||||
{{error.message}}
|
||||
</Hds::Text::Body>
|
||||
{{/each}}
|
||||
</A.Description>
|
||||
</Hds::Alert>
|
||||
{{/if}}
|
||||
|
||||
</:body>
|
||||
<:action>
|
||||
<Hds::Button
|
||||
@text={{t
|
||||
(if this.isClientAgentStatusRunning 'actions.pause' 'actions.resume')
|
||||
}}
|
||||
@color='secondary'
|
||||
@icon={{this.buttonIcon}}
|
||||
@size='medium'
|
||||
disabled={{(is-loading)}}
|
||||
{{on 'click' this.changeClientAgentState}}
|
||||
/>
|
||||
</:action>
|
||||
</SettingsCard>
|
||||
{{/if}}
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { loading } from 'ember-loading';
|
||||
import { notifyError } from 'core/decorators/notify';
|
||||
|
||||
export default class SettingsCardClientAgentComponent extends Component {
|
||||
// =services
|
||||
@service ipc;
|
||||
@service router;
|
||||
@service loading;
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* Checks if the status returned by the client agent is "running"
|
||||
* @return {boolean}
|
||||
*/
|
||||
get isClientAgentStatusRunning() {
|
||||
return this.args.model.clientAgentStatus?.status === 'running';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the client agent is actively running
|
||||
* @return {boolean}
|
||||
*/
|
||||
get isClientAgentActive() {
|
||||
return (
|
||||
this.args.model.clientAgentStatus?.status === 'running' ||
|
||||
this.args.model.clientAgentStatus?.status === 'paused'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the button icon based on the current state of the client agent
|
||||
* @return {string}
|
||||
*/
|
||||
get buttonIcon() {
|
||||
if (this.loading.isLoading) {
|
||||
return 'loading';
|
||||
} else {
|
||||
return this.isClientAgentStatusRunning ? 'pause' : 'play';
|
||||
}
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Changes the state of the client agent based on the current state.
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
@action
|
||||
@loading
|
||||
@notifyError(({ message }) => message)
|
||||
async changeClientAgentState() {
|
||||
if (this.isClientAgentStatusRunning) {
|
||||
await this.ipc.invoke('pauseClientAgent');
|
||||
} else {
|
||||
await this.ipc.invoke('resumeClientAgent');
|
||||
}
|
||||
await this.router.refresh();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@
|
||||
}}
|
||||
|
||||
<Hds::Card::Container @level='mid' @hasBorder={{true}}>
|
||||
|
||||
<div class='header-and-icon'>
|
||||
<Hds::IconTile @icon={{@icon}} @size='small' />
|
||||
<Hds::Text::Display @tag='h4'>
|
||||
@@ -20,11 +19,10 @@
|
||||
{{yield to='action'}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
</div>
|
||||
|
||||
{{#if @description}}
|
||||
<Hds::Text::Body @tag='p' @color='faint'>
|
||||
<Hds::Text::Body @tag='p' @color='faint' @size='100'>
|
||||
{{@description}}
|
||||
</Hds::Text::Body>
|
||||
{{/if}}
|
||||
@@ -35,5 +33,4 @@
|
||||
{{yield to='body'}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
</Hds::Card::Container>
|
||||
@@ -13,11 +13,21 @@ export default class SettingsCardLogsComponent extends Component {
|
||||
|
||||
// =attributes
|
||||
|
||||
/**
|
||||
* Returns the list of available log levels
|
||||
* @return {string[]}
|
||||
*/
|
||||
get logLevels() {
|
||||
return ['error', 'warn', 'info', 'debug'];
|
||||
}
|
||||
|
||||
// =methods
|
||||
|
||||
/**
|
||||
* Sets the log level to the selected value
|
||||
* @param value
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
@action
|
||||
async selectLogLevel({ target: { value } }) {
|
||||
await this.ipc.invoke('setLogLevel', value);
|
||||
|
||||
@@ -13,6 +13,7 @@ export default class SettingsUserInfoComponent extends Component {
|
||||
@service session;
|
||||
@service intl;
|
||||
|
||||
// =attributes
|
||||
/**
|
||||
* Returns the username of the current user
|
||||
* @type {string}
|
||||
|
||||
@@ -64,6 +64,8 @@ export default class ScopesScopeProjectsRoute extends Route {
|
||||
);
|
||||
if (isClientAgentRunning) {
|
||||
this.job.start();
|
||||
} else {
|
||||
this.job.stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,26 +14,38 @@ export default class ScopesScopeProjectsSettingsIndexRoute extends Route {
|
||||
|
||||
// =methods
|
||||
async model() {
|
||||
let cacheDaemonStatus,
|
||||
formattedCacheVersion,
|
||||
formattedCliVersion,
|
||||
formattedDesktopVersion,
|
||||
cacheDaemonErrors = [];
|
||||
|
||||
const { versionNumber: cliVersion } =
|
||||
await this.ipc.invoke('getCliVersion');
|
||||
formattedCliVersion = `v${cliVersion}`;
|
||||
|
||||
const { desktopVersion } = await this.ipc.invoke('getDesktopVersion');
|
||||
formattedDesktopVersion = `v${desktopVersion}`;
|
||||
const cacheDaemonStatus = await this.getCacheStatus();
|
||||
const clientAgentStatus = await this.getClientAgentStatus();
|
||||
|
||||
const logLevel = await this.ipc.invoke('getLogLevel');
|
||||
const logPath = await this.ipc.invoke('getLogPath');
|
||||
const serverInformation = this.clusterUrl.rendererClusterUrl;
|
||||
|
||||
return {
|
||||
desktopVersion: `v${desktopVersion}`,
|
||||
cliVersion: `v${cliVersion}`,
|
||||
cacheDaemonStatus,
|
||||
clientAgentStatus,
|
||||
logLevel,
|
||||
logPath,
|
||||
serverInformation,
|
||||
};
|
||||
}
|
||||
|
||||
async getCacheStatus() {
|
||||
let cacheDaemonStatus;
|
||||
let cacheDaemonStatusError = [];
|
||||
try {
|
||||
cacheDaemonStatus = await this.ipc.invoke('cacheDaemonStatus');
|
||||
} catch (e) {
|
||||
cacheDaemonErrors.push(e);
|
||||
cacheDaemonStatusError.push(e);
|
||||
}
|
||||
const { version, users } = cacheDaemonStatus ?? {};
|
||||
formattedCacheVersion = version?.match(/v\d+\.\d+\.\d+/)?.[0];
|
||||
const cacheVersion = version?.match(/v\d+\.\d+\.\d+/)?.[0];
|
||||
|
||||
// Grab the errors from each resource
|
||||
const userId = this.session.data.authenticated.user_id;
|
||||
@@ -45,20 +57,24 @@ export default class ScopesScopeProjectsSettingsIndexRoute extends Route {
|
||||
message: resource.last_error.error,
|
||||
name: resource.name,
|
||||
})) ?? [];
|
||||
cacheDaemonErrors = [...cacheDaemonErrors, ...resourceErrors];
|
||||
|
||||
const logLevel = await this.ipc.invoke('getLogLevel');
|
||||
const logPath = await this.ipc.invoke('getLogPath');
|
||||
const serverInformation = this.clusterUrl.rendererClusterUrl;
|
||||
const errors = [...cacheDaemonStatusError, ...resourceErrors];
|
||||
return { version: cacheVersion, errors };
|
||||
}
|
||||
|
||||
return {
|
||||
cacheDaemonErrors,
|
||||
formattedCliVersion,
|
||||
formattedCacheVersion,
|
||||
formattedDesktopVersion,
|
||||
logLevel,
|
||||
logPath,
|
||||
serverInformation,
|
||||
};
|
||||
async getClientAgentStatus() {
|
||||
let clientAgentStatus;
|
||||
let clientAgentStatusError = [];
|
||||
try {
|
||||
clientAgentStatus = await this.ipc.invoke('clientAgentStatus');
|
||||
} catch (e) {
|
||||
clientAgentStatusError.push(e);
|
||||
}
|
||||
|
||||
const { version, status, errors: errorMessages } = clientAgentStatus ?? {};
|
||||
const statusErrors = errorMessages?.map((message) => ({ message })) ?? [];
|
||||
|
||||
const errors = [...clientAgentStatusError, ...statusErrors];
|
||||
return { version: `v${version}`, status, errors };
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
}}
|
||||
|
||||
<SettingsCard::Application @model={{@model}} @toggle={{this.toggleTheme}} />
|
||||
<SettingsCard::ClientAgent @model={{@model}} />
|
||||
<SettingsCard::User />
|
||||
<SettingsCard::Server @model={{@model}} />
|
||||
<SettingsCard::Logs @model={{@model}} />
|
||||
@@ -99,10 +99,11 @@ module.exports = {
|
||||
* Spawn a child process and return a promise.
|
||||
* Resolves on any output from stdout or stderr.
|
||||
* @param command
|
||||
* @param options
|
||||
*/
|
||||
spawn(command) {
|
||||
spawn(command, options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const childProcess = spawn(path(), command);
|
||||
const childProcess = spawn(path(), command, options);
|
||||
childProcess.stdout.on('data', (data) => {
|
||||
resolve({ childProcess, stdout: data.toString() });
|
||||
});
|
||||
|
||||
@@ -197,6 +197,21 @@ handle('isClientAgentRunning', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the client agent's status
|
||||
*/
|
||||
handle('clientAgentStatus', async () => clientAgentDaemonManager.status());
|
||||
|
||||
/**
|
||||
* Pauses the client agent
|
||||
*/
|
||||
handle('pauseClientAgent', async () => clientAgentDaemonManager.pause());
|
||||
|
||||
/**
|
||||
* Resumes the client agent
|
||||
*/
|
||||
handle('resumeClientAgent', async () => clientAgentDaemonManager.resume());
|
||||
|
||||
/**
|
||||
* Gets boundary cli version
|
||||
*/
|
||||
@@ -210,7 +225,7 @@ handle('getDesktopVersion', async () => ({ desktopVersion: releaseVersion }));
|
||||
/**
|
||||
* Returns the Cache daemon status
|
||||
*/
|
||||
handle('cacheDaemonStatus', async () => await cacheDaemonManager.status());
|
||||
handle('cacheDaemonStatus', async () => cacheDaemonManager.status());
|
||||
|
||||
/**
|
||||
* Returns the current log level
|
||||
|
||||
@@ -15,7 +15,11 @@ class ClientAgentDaemonManager {
|
||||
*/
|
||||
async status() {
|
||||
const clientAgentStatusCommand = ['client-agent', 'status', '-format=json'];
|
||||
const { stdout, stderr } = spawnSync(clientAgentStatusCommand);
|
||||
// The status command can take a while to execute if it's not running as it
|
||||
// retries multiple times so we should set a timeout to not wait too long
|
||||
const { stderr, stdout } = await spawn(clientAgentStatusCommand, {
|
||||
timeout: 500,
|
||||
});
|
||||
|
||||
let parsedResponse = jsonify(stdout);
|
||||
if (parsedResponse?.status_code === 200) {
|
||||
@@ -47,6 +51,46 @@ class ClientAgentDaemonManager {
|
||||
log.warn('Client Agent Sessions:', stderr);
|
||||
return generateErrorPromise(stderr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pauses the client agent daemon.
|
||||
* @return {Promise<*>}
|
||||
*/
|
||||
async pause() {
|
||||
const clientAgentPauseCommand = ['client-agent', 'pause', '-format=json'];
|
||||
// We use spawn here as pausing can take a while to execute and using spawnSync
|
||||
// can cause the app to completely freeze and be unresponsive during that time
|
||||
const { stderr, stdout } = await spawn(clientAgentPauseCommand, {
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
let parsedResponse = jsonify(stdout);
|
||||
if (parsedResponse?.status_code === 200) {
|
||||
return parsedResponse.item;
|
||||
}
|
||||
|
||||
log.warn('Client Agent Pause:', stderr);
|
||||
return generateErrorPromise(stderr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resumes the client agent daemon.
|
||||
* @return {Promise<*>}
|
||||
*/
|
||||
async resume() {
|
||||
const clientAgentResumeCommand = ['client-agent', 'resume', '-format=json'];
|
||||
const { stderr, stdout } = await spawn(clientAgentResumeCommand, {
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
let parsedResponse = jsonify(stdout);
|
||||
if (parsedResponse?.status_code === 200) {
|
||||
return parsedResponse.item;
|
||||
}
|
||||
|
||||
log.warn('Client Agent Resume:', stderr);
|
||||
return generateErrorPromise(stderr);
|
||||
}
|
||||
}
|
||||
|
||||
// Export an instance so we get a singleton
|
||||
|
||||
@@ -34,6 +34,27 @@ class MockIPC {
|
||||
isClientAgentRunning() {
|
||||
return false;
|
||||
}
|
||||
cacheDaemonStatus() {
|
||||
return { version: 'Boundary v1.0.0' };
|
||||
}
|
||||
getCliVersion() {
|
||||
return { versionNumber: '1.0.0' };
|
||||
}
|
||||
getDesktopVersion() {
|
||||
return { desktopVersion: '1.0.0' };
|
||||
}
|
||||
getLogLevel() {
|
||||
return 'info';
|
||||
}
|
||||
getLogPath() {
|
||||
return '~/.config/Boundary/logs/desktop-client.log';
|
||||
}
|
||||
setLogLevel() {}
|
||||
clientAgentStatus() {
|
||||
return { version: '0.0.1-dev', status: 'running' };
|
||||
}
|
||||
pauseClientAgent() {}
|
||||
resumeClientAgent() {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,13 +18,18 @@ module('Integration | Component | settings-card/application', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupIntl(hooks);
|
||||
|
||||
let model;
|
||||
|
||||
hooks.beforeEach(() => {
|
||||
model = {
|
||||
cacheDaemonStatus: { version: 'v4.0.0', errors: [] },
|
||||
desktopVersion: 'v2.0.0',
|
||||
cliVersion: 'v3.0.0',
|
||||
};
|
||||
});
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
this.set('model', {
|
||||
cacheDaemonErrors: [],
|
||||
formattedDesktopVersion: 'v2.0.0',
|
||||
formattedCliVersion: 'v3.0.0',
|
||||
formattedCacheVersion: 'v4.0.0',
|
||||
});
|
||||
this.set('model', model);
|
||||
this.set('toggleTheme', () => {});
|
||||
|
||||
await render(
|
||||
@@ -37,21 +42,17 @@ module('Integration | Component | settings-card/application', function (hooks) {
|
||||
});
|
||||
|
||||
test('it renders errors', async function (assert) {
|
||||
this.set('model', {
|
||||
cacheDaemonErrors: [
|
||||
{
|
||||
message: `this is a resolvable alias error`,
|
||||
name: 'resolvable-alias',
|
||||
},
|
||||
{
|
||||
message: `this is a target error`,
|
||||
name: 'target',
|
||||
},
|
||||
],
|
||||
formattedDesktopVersion: 'v2.0.0',
|
||||
formattedCliVersion: 'v3.0.0',
|
||||
formattedCacheVersion: 'v4.0.0',
|
||||
});
|
||||
model.cacheDaemonStatus.errors = [
|
||||
{
|
||||
message: `this is a resolvable alias error`,
|
||||
name: 'resolvable-alias',
|
||||
},
|
||||
{
|
||||
message: `this is a target error`,
|
||||
name: 'target',
|
||||
},
|
||||
];
|
||||
this.set('model', model);
|
||||
this.set('toggleTheme', () => {});
|
||||
|
||||
await render(
|
||||
@@ -66,12 +67,7 @@ module('Integration | Component | settings-card/application', function (hooks) {
|
||||
});
|
||||
|
||||
test('it indicates when cache daemon is running', async function (assert) {
|
||||
this.set('model', {
|
||||
cacheDaemonErrors: [],
|
||||
formattedDesktopVersion: 'v2.0.0',
|
||||
formattedCliVersion: 'v3.0.0',
|
||||
formattedCacheVersion: 'v4.0.0',
|
||||
});
|
||||
this.set('model', model);
|
||||
this.set('toggleTheme', () => {});
|
||||
|
||||
await render(
|
||||
@@ -80,12 +76,8 @@ module('Integration | Component | settings-card/application', function (hooks) {
|
||||
|
||||
assert.dom(CACHE_DAEMON_VERSION).hasText('v4.0.0 Running');
|
||||
|
||||
this.set('model', {
|
||||
cacheDaemonErrors: [],
|
||||
formattedDesktopVersion: 'v2.0.0',
|
||||
formattedCliVersion: 'v3.0.0',
|
||||
formattedCacheVersion: '',
|
||||
});
|
||||
model.cacheDaemonStatus.version = '';
|
||||
this.set('model', structuredClone(model));
|
||||
assert.dom(CACHE_DAEMON_VERSION).hasText('Not Running');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Copyright (c) HashiCorp, Inc.
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'ember-qunit';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { setupIntl } from 'ember-intl/test-support';
|
||||
|
||||
const STATUS_BADGE = '.hds-badge';
|
||||
const ACTION_BUTTON = 'button';
|
||||
const CARD_CONTAINER = 'hds-card__container';
|
||||
const ERROR_MESSAGES = '.hds-alert__description > p';
|
||||
const LAST_ERROR_MESSAGE = '.hds-alert__description > p:last-child';
|
||||
|
||||
module(
|
||||
'Integration | Component | settings-card/client-agent',
|
||||
function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupIntl(hooks);
|
||||
|
||||
test('it renders running badge and pause button', async function (assert) {
|
||||
this.set('model', {
|
||||
clientAgentStatus: {
|
||||
status: 'running',
|
||||
errors: [],
|
||||
},
|
||||
});
|
||||
|
||||
await render(hbs`<SettingsCard::ClientAgent @model={{this.model}}/>`);
|
||||
|
||||
assert.dom(STATUS_BADGE).hasText('Running');
|
||||
assert.dom(ACTION_BUTTON).hasText('Pause');
|
||||
});
|
||||
|
||||
test('it renders paused badge and resume button', async function (assert) {
|
||||
this.set('model', {
|
||||
clientAgentStatus: {
|
||||
status: 'paused',
|
||||
errors: [],
|
||||
},
|
||||
});
|
||||
|
||||
await render(hbs`<SettingsCard::ClientAgent @model={{this.model}}/>`);
|
||||
|
||||
assert.dom(STATUS_BADGE).hasText('Paused');
|
||||
assert.dom(ACTION_BUTTON).hasText('Resume');
|
||||
});
|
||||
|
||||
test('it does not render component if there is no status', async function (assert) {
|
||||
this.set('model', {
|
||||
clientAgentStatus: {
|
||||
errors: [{ message: 'agent is offline' }],
|
||||
},
|
||||
});
|
||||
|
||||
await render(hbs`<SettingsCard::ClientAgent @model={{this.model}}/>`);
|
||||
|
||||
assert.dom(STATUS_BADGE).doesNotExist();
|
||||
assert.dom(ACTION_BUTTON).doesNotExist();
|
||||
assert.dom(CARD_CONTAINER).doesNotExist();
|
||||
});
|
||||
|
||||
test('it renders errors', async function (assert) {
|
||||
this.set('model', {
|
||||
clientAgentStatus: {
|
||||
status: 'running',
|
||||
errors: [
|
||||
{ message: 'agent is wonky' },
|
||||
{ message: 'another error' },
|
||||
{ message: 'big oof' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
await render(hbs`<SettingsCard::ClientAgent @model={{this.model}}/>`);
|
||||
|
||||
assert.dom(ERROR_MESSAGES).isVisible({ count: 3 });
|
||||
assert.dom(LAST_ERROR_MESSAGE).hasText('big oof');
|
||||
});
|
||||
},
|
||||
);
|
||||
Reference in new issue
Block a user