Optimise Desktop API queries (#1707)
* fix: 🐛 increase auth form width, which was too narrow * chore: 🤖 optimize targets route queries * chore: 🤖 optimize sessions route queries * chore: 🤖 disable polling in sessions route * chore: 🤖 disable polling in targets route * refactor: 💡 update tests for new queries and removing timing
This commit is contained in:
1 parent
547d44f1bd
commit
863c080196
8 files changed
+335
-389
No files matched your search
@@ -26,12 +26,18 @@ export default factory.extend({
|
|||||||
withAssociations: trait({
|
withAssociations: trait({
|
||||||
afterCreate(record, server) {
|
afterCreate(record, server) {
|
||||||
const scopeId = record.scopeId;
|
const scopeId = record.scopeId;
|
||||||
if (record.scope.type === 'project') {
|
|
||||||
const orgScopeId = record.scope?.scope.id;
|
// Assign a user to this session if not already assigned.
|
||||||
const user = server.schema.users.where({ scopeId: orgScopeId })
|
if (record.scope && !record.user) {
|
||||||
.models[0];
|
const scopeId =
|
||||||
|
record.scope.type === 'project'
|
||||||
|
? record.scope?.scope.id
|
||||||
|
: record.scope.id;
|
||||||
|
const user = server.schema.users.where({ scopeId }).models[0];
|
||||||
if (user) record.update({ user });
|
if (user) record.update({ user });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Assign target and host set to this session, if not already assigned.
|
||||||
const target = server.schema.targets.where({ scopeId }).models[0];
|
const target = server.schema.targets.where({ scopeId }).models[0];
|
||||||
if (target) {
|
if (target) {
|
||||||
record.update({ target });
|
record.update({ target });
|
||||||
|
|||||||
@@ -102,6 +102,9 @@ export default function (server) {
|
|||||||
server.create('target', { scope, address: '0.0.0.0' });
|
server.create('target', { scope, address: '0.0.0.0' });
|
||||||
// Sessions have target data. Create it after targets.
|
// Sessions have target data. Create it after targets.
|
||||||
server.createList('session', 4, { scope }, 'withAssociations');
|
server.createList('session', 4, { scope }, 'withAssociations');
|
||||||
|
// Also create sessions for "authenticateduser" for Desktop,
|
||||||
|
// which filters user_id by the currently authenticated user.
|
||||||
|
server.createList('session', 1, { scope, user }, 'withAssociations');
|
||||||
// Create IAM resources for project
|
// Create IAM resources for project
|
||||||
server.createList('group', 3, { scope });
|
server.createList('group', 3, { scope });
|
||||||
server.createList('role', 3, { scope });
|
server.createList('role', 3, { scope });
|
||||||
|
|||||||
@@ -6,12 +6,8 @@
|
|||||||
import Route from '@ember/routing/route';
|
import Route from '@ember/routing/route';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import runEvery from 'ember-pollster/decorators/route/run-every';
|
|
||||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||||
import { resourceFilter } from 'core/decorators/resource-filter';
|
import { resourceFilter } from 'core/decorators/resource-filter';
|
||||||
import config from '../../../../config/environment';
|
|
||||||
|
|
||||||
const POLL_TIMEOUT_SECONDS = config.sessionPollingTimeoutSeconds;
|
|
||||||
|
|
||||||
export default class ScopesScopeProjectsSessionsRoute extends Route {
|
export default class ScopesScopeProjectsSessionsRoute extends Route {
|
||||||
// =services
|
// =services
|
||||||
@@ -47,32 +43,42 @@ export default class ScopesScopeProjectsSessionsRoute extends Route {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all sessions under the current scope and encapsulates them into
|
* Loads all sessions under current scope for the current user.
|
||||||
* an array of objects filtering to current user
|
*
|
||||||
* @return {Promise{SessionModel[]}}
|
* NOTE: previously, sessions were filtered only with API filter queries.
|
||||||
|
* In an effort to offload processing from the controller, sessions
|
||||||
|
* are now filtered on the client by projects and status,
|
||||||
|
* while user_id filtering remains server side.
|
||||||
|
*
|
||||||
|
* @return {Promise{[SessionModel]}}
|
||||||
*/
|
*/
|
||||||
async model() {
|
async model() {
|
||||||
const { status } = this;
|
|
||||||
const { id: scope_id } = this.modelFor('scopes.scope');
|
const { id: scope_id } = this.modelFor('scopes.scope');
|
||||||
const { user_id } = this.session.data.authenticated;
|
const queryOptions = {
|
||||||
const projects = this.project || [];
|
|
||||||
const filters = {
|
|
||||||
user_id,
|
|
||||||
status,
|
|
||||||
scope_id: projects.map(({ id }) => id),
|
|
||||||
};
|
|
||||||
const options = {
|
|
||||||
recursive: true,
|
|
||||||
scope_id,
|
scope_id,
|
||||||
include_terminated: filters.status?.includes('terminated'),
|
recursive: true,
|
||||||
|
include_terminated: this.status?.includes('terminated'),
|
||||||
};
|
};
|
||||||
await this.store.query('target', { recursive: true, scope_id });
|
|
||||||
return await this.resourceFilterStore.queryBy('session', filters, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
@runEvery(POLL_TIMEOUT_SECONDS * 1000)
|
// Recursively query sessions within the current scope for the current user
|
||||||
poller() {
|
let sessions = await this.resourceFilterStore.queryBy(
|
||||||
return this.refresh();
|
'session',
|
||||||
|
{ user_id: this.session.data.authenticated.user_id },
|
||||||
|
queryOptions
|
||||||
|
);
|
||||||
|
|
||||||
|
// If project filters are selected, filter sessions by the selected projects
|
||||||
|
if (this.project?.length) {
|
||||||
|
const projectIDs = this.project.map((p) => p?.id);
|
||||||
|
sessions = sessions.filter((s) => projectIDs.includes(s?.scopeID));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If status filters are selected, filter sessions by the selected statuses
|
||||||
|
if (this.status?.length) {
|
||||||
|
sessions = sessions.filter((s) => this.status.includes(s?.status));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sessions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
// =actions
|
||||||
|
|||||||
@@ -7,21 +7,18 @@ import Route from '@ember/routing/route';
|
|||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
import { action } from '@ember/object';
|
import { action } from '@ember/object';
|
||||||
import { resourceFilter } from 'core/decorators/resource-filter';
|
import { resourceFilter } from 'core/decorators/resource-filter';
|
||||||
import runEvery from 'ember-pollster/decorators/route/run-every';
|
|
||||||
import config from '../../../../config/environment';
|
|
||||||
|
|
||||||
const POLL_TIMEOUT_SECONDS = config.sessionPollingTimeoutSeconds;
|
|
||||||
|
|
||||||
export default class ScopesScopeProjectsTargetsRoute extends Route {
|
export default class ScopesScopeProjectsTargetsRoute extends Route {
|
||||||
// =services
|
// =services
|
||||||
|
|
||||||
@service store;
|
@service can;
|
||||||
@service ipc;
|
|
||||||
@service session;
|
|
||||||
@service clusterUrl;
|
@service clusterUrl;
|
||||||
@service confirm;
|
@service confirm;
|
||||||
|
@service ipc;
|
||||||
@service resourceFilterStore;
|
@service resourceFilterStore;
|
||||||
@service router;
|
@service router;
|
||||||
|
@service session;
|
||||||
|
@service store;
|
||||||
|
|
||||||
// =attributes
|
// =attributes
|
||||||
|
|
||||||
@@ -43,36 +40,30 @@ export default class ScopesScopeProjectsTargetsRoute extends Route {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads all targets under current scope.
|
* Loads all targets under current scope.
|
||||||
|
*
|
||||||
|
* NOTE: previously, targets were filtered with API filter queries.
|
||||||
|
* In an effort to offload processing from the controller, targets
|
||||||
|
* are now filtered on the client by projects and authorized_actions.
|
||||||
|
*
|
||||||
* @return {Promise{[TargetModel]}}
|
* @return {Promise{[TargetModel]}}
|
||||||
*/
|
*/
|
||||||
async model() {
|
async model() {
|
||||||
const { id: scope_id } = this.modelFor('scopes.scope');
|
const { id: scope_id } = this.modelFor('scopes.scope');
|
||||||
const { user_id } = this.session.data.authenticated;
|
const queryOptions = { scope_id, recursive: true };
|
||||||
const projects = this.project || [];
|
|
||||||
await this.resourceFilterStore.queryBy(
|
|
||||||
'session',
|
|
||||||
{ user_id },
|
|
||||||
{
|
|
||||||
recursive: true,
|
|
||||||
scope_id,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return this.resourceFilterStore.queryBy(
|
|
||||||
'target',
|
|
||||||
{
|
|
||||||
scope_id: projects.map(({ id }) => id),
|
|
||||||
authorized_actions: [{ contains: 'authorize-session' }],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
recursive: true,
|
|
||||||
scope_id,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@runEvery(POLL_TIMEOUT_SECONDS * 1000)
|
// Recursively query all targets within the current scope
|
||||||
poller() {
|
let targets = await this.store.query('target', queryOptions);
|
||||||
return this.refresh();
|
|
||||||
|
// Filter out targets to which users do not have the connect ability
|
||||||
|
targets = targets.filter((t) => this.can.can('connect target', t));
|
||||||
|
|
||||||
|
// If project filters are selected, filter targets by the selected projects
|
||||||
|
if (this.project?.length) {
|
||||||
|
const projectIDs = this.project.map((p) => p?.id);
|
||||||
|
targets = targets.filter((t) => projectIDs.includes(t?.scopeID));
|
||||||
|
}
|
||||||
|
|
||||||
|
return targets;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =actions
|
// =actions
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { visit, currentURL, click, find, findAll } from '@ember/test-helpers';
|
import { visit, currentURL, click, find, findAll } from '@ember/test-helpers';
|
||||||
import { later, _cancelTimers } from '@ember/runloop';
|
|
||||||
import { setupApplicationTest } from 'ember-qunit';
|
import { setupApplicationTest } from 'ember-qunit';
|
||||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||||
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
||||||
@@ -169,31 +168,19 @@ module('Acceptance | projects | targets | hosts', function (hooks) {
|
|||||||
test('visiting index', async function (assert) {
|
test('visiting index', async function (assert) {
|
||||||
assert.expect(2);
|
assert.expect(2);
|
||||||
const hostsCount = this.server.schema.hosts.all().models.length;
|
const hostsCount = this.server.schema.hosts.all().models.length;
|
||||||
// This later/cancelTimers technique allows us to test a page with
|
|
||||||
// active polling. Normally an acceptance test waits for all runloop timers
|
|
||||||
// to stop before returning from an awaited test, but polling means that
|
|
||||||
// runloop timers exist indefinitely. We thus schedule a cancelation before
|
|
||||||
// proceeding with our tests.
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
// await a11yAudit();
|
|
||||||
assert.strictEqual(currentURL(), urls.hosts);
|
|
||||||
assert.strictEqual(findAll('tbody tr').length, hostsCount);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.hosts);
|
await visit(urls.hosts);
|
||||||
|
assert.strictEqual(currentURL(), urls.hosts);
|
||||||
|
assert.strictEqual(findAll('tbody tr').length, hostsCount);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visiting empty hosts', async function (assert) {
|
test('visiting empty hosts', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
instances.target.update({ hostSets: [] });
|
instances.target.update({ hostSets: [] });
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
assert.ok(
|
|
||||||
find('.rose-message-title').textContent.trim(),
|
|
||||||
'No Hosts Available'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.hosts);
|
await visit(urls.hosts);
|
||||||
|
assert.ok(
|
||||||
|
find('.rose-message-title').textContent.trim(),
|
||||||
|
'No Hosts Available'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('connecting to a host', async function (assert) {
|
test('connecting to a host', async function (assert) {
|
||||||
@@ -208,24 +195,21 @@ module('Acceptance | projects | targets | hosts', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click(
|
|
||||||
'tbody tr:first-child td:last-child button',
|
|
||||||
'Activate connect mode'
|
|
||||||
);
|
|
||||||
assert.ok(find('.dialog-detail'), 'Success dialog');
|
|
||||||
assert.strictEqual(
|
|
||||||
find('.rose-dialog-footer .rose-button-secondary').textContent.trim(),
|
|
||||||
'Close',
|
|
||||||
'Cannot retry'
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
|
||||||
find('.rose-dialog-body .copyable-content').textContent.trim(),
|
|
||||||
'a_123:p_123'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.hosts);
|
await visit(urls.hosts);
|
||||||
|
await click(
|
||||||
|
'tbody tr:first-child td:last-child button',
|
||||||
|
'Activate connect mode'
|
||||||
|
);
|
||||||
|
assert.ok(find('.dialog-detail'), 'Success dialog');
|
||||||
|
assert.strictEqual(
|
||||||
|
find('.rose-dialog-footer .rose-button-secondary').textContent.trim(),
|
||||||
|
'Close',
|
||||||
|
'Cannot retry'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
find('.rose-dialog-body .copyable-content').textContent.trim(),
|
||||||
|
'a_123:p_123'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles cli error on connect', async function (assert) {
|
test('handles cli error on connect', async function (assert) {
|
||||||
@@ -234,27 +218,24 @@ module('Acceptance | projects | targets | hosts', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click(
|
|
||||||
'tbody tr:first-child td:last-child button',
|
|
||||||
'Activate connect mode'
|
|
||||||
);
|
|
||||||
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
|
||||||
const dialogButtons = findAll('.rose-dialog-footer button');
|
|
||||||
assert.strictEqual(dialogButtons.length, 2);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[0].textContent.trim(),
|
|
||||||
'Retry',
|
|
||||||
'Can retry'
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[1].textContent.trim(),
|
|
||||||
'Cancel',
|
|
||||||
'Can cancel'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.hosts);
|
await visit(urls.hosts);
|
||||||
|
await click(
|
||||||
|
'tbody tr:first-child td:last-child button',
|
||||||
|
'Activate connect mode'
|
||||||
|
);
|
||||||
|
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
||||||
|
const dialogButtons = findAll('.rose-dialog-footer button');
|
||||||
|
assert.strictEqual(dialogButtons.length, 2);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[0].textContent.trim(),
|
||||||
|
'Retry',
|
||||||
|
'Can retry'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[1].textContent.trim(),
|
||||||
|
'Cancel',
|
||||||
|
'Can cancel'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles connect error', async function (assert) {
|
test('handles connect error', async function (assert) {
|
||||||
@@ -264,27 +245,24 @@ module('Acceptance | projects | targets | hosts', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click(
|
|
||||||
'tbody tr:first-child td:last-child button',
|
|
||||||
'Activate connect mode'
|
|
||||||
);
|
|
||||||
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
|
||||||
const dialogButtons = findAll('.rose-dialog-footer button');
|
|
||||||
assert.strictEqual(dialogButtons.length, 2);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[0].textContent.trim(),
|
|
||||||
'Retry',
|
|
||||||
'Can retry'
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[1].textContent.trim(),
|
|
||||||
'Cancel',
|
|
||||||
'Can cancel'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.hosts);
|
await visit(urls.hosts);
|
||||||
|
await click(
|
||||||
|
'tbody tr:first-child td:last-child button',
|
||||||
|
'Activate connect mode'
|
||||||
|
);
|
||||||
|
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
||||||
|
const dialogButtons = findAll('.rose-dialog-footer button');
|
||||||
|
assert.strictEqual(dialogButtons.length, 2);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[0].textContent.trim(),
|
||||||
|
'Retry',
|
||||||
|
'Can retry'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[1].textContent.trim(),
|
||||||
|
'Cancel',
|
||||||
|
'Can cancel'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can retry on error', async function (assert) {
|
test('can retry on error', async function (assert) {
|
||||||
@@ -293,17 +271,14 @@ module('Acceptance | projects | targets | hosts', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click(
|
|
||||||
'tbody tr:first-child td:last-child button',
|
|
||||||
'Activate connect mode'
|
|
||||||
);
|
|
||||||
const firstErrorDialog = find('.rose-dialog');
|
|
||||||
await click('.rose-dialog footer .rose-button-primary', 'Retry');
|
|
||||||
const secondErrorDialog = find('.rose-dialog');
|
|
||||||
assert.notEqual(secondErrorDialog.id, firstErrorDialog.id);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.hosts);
|
await visit(urls.hosts);
|
||||||
|
await click(
|
||||||
|
'tbody tr:first-child td:last-child button',
|
||||||
|
'Activate connect mode'
|
||||||
|
);
|
||||||
|
const firstErrorDialog = find('.rose-dialog');
|
||||||
|
await click('.rose-dialog footer .rose-button-primary', 'Retry');
|
||||||
|
const secondErrorDialog = find('.rose-dialog');
|
||||||
|
assert.notEqual(secondErrorDialog.id, firstErrorDialog.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { visit, currentURL, click, find, findAll } from '@ember/test-helpers';
|
import { visit, currentURL, click, find, findAll } from '@ember/test-helpers';
|
||||||
import { later, _cancelTimers } from '@ember/runloop';
|
|
||||||
import { setupApplicationTest } from 'ember-qunit';
|
import { setupApplicationTest } from 'ember-qunit';
|
||||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||||
import { Response } from 'miragejs';
|
import { Response } from 'miragejs';
|
||||||
@@ -173,107 +172,140 @@ module('Acceptance | projects | targets | sessions', function (hooks) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('visiting index', async function (assert) {
|
test('visiting index', async function (assert) {
|
||||||
assert.expect(2);
|
assert.expect(1);
|
||||||
const sessionsCount = this.server.schema.sessions.all().models.length;
|
|
||||||
// This later/cancelTimers technique allows us to test a page with
|
|
||||||
// active polling. Normally an acceptance test waits for all runloop timers
|
|
||||||
// to stop before returning from an awaited test, but polling means that
|
|
||||||
// runloop timers exist indefinitely. We thus schedule a cancelation before
|
|
||||||
// proceeding with our tests.
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
// await a11yAudit();
|
|
||||||
assert.strictEqual(currentURL(), urls.sessions);
|
|
||||||
assert.strictEqual(findAll('tbody tr').length, sessionsCount);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
assert.strictEqual(currentURL(), urls.sessions);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visiting index redirects to sessions', async function (assert) {
|
test('visiting index redirects to sessions', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
// await a11yAudit();
|
|
||||||
assert.strictEqual(currentURL(), urls.sessions);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.target);
|
await visit(urls.target);
|
||||||
|
assert.strictEqual(currentURL(), urls.sessions);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visiting empty sessions', async function (assert) {
|
test('visiting empty sessions', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
this.server.get('/sessions', () => new Response(200));
|
this.server.get('/sessions', () => new Response(200));
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
assert.ok(
|
|
||||||
find('.rose-message-title').textContent.trim(),
|
|
||||||
'No Sessions Available'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
assert.ok(
|
||||||
|
find('.rose-message-title').textContent.trim(),
|
||||||
|
'No Sessions Available'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can identify target with active sessions', async function (assert) {
|
test('can identify target with active sessions', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||||
assert.ok(find('.rose-layout-page-header .hds-badge--color-success'));
|
stubs.ipcService.withArgs('connect').returns({
|
||||||
}, 750);
|
session_id: instances.session.id,
|
||||||
|
address: 'a_123',
|
||||||
|
port: 'p_123',
|
||||||
|
protocol: 'tcp',
|
||||||
|
});
|
||||||
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
|
confirmService.enabled = true;
|
||||||
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
||||||
|
await click('.rose-dialog-footer .rose-button-secondary');
|
||||||
|
assert.ok(find('.rose-layout-page-header .hds-badge--color-success'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can identify target with pending sessions', async function (assert) {
|
test('can identify target with pending sessions', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
|
|
||||||
instances.session.update({ status: 'pending' });
|
instances.session.update({ status: 'pending' });
|
||||||
later(async () => {
|
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||||
_cancelTimers();
|
stubs.ipcService.withArgs('connect').returns({
|
||||||
assert.ok(find('.rose-layout-page-header .hds-badge--color-success'));
|
session_id: instances.session.id,
|
||||||
}, 750);
|
address: 'a_123',
|
||||||
|
port: 'p_123',
|
||||||
|
protocol: 'tcp',
|
||||||
|
});
|
||||||
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
|
confirmService.enabled = true;
|
||||||
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
||||||
|
await click('.rose-dialog-footer .rose-button-secondary');
|
||||||
|
assert.ok(find('.rose-layout-page-header .hds-badge--color-success'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cannot identify target with terminated sessions', async function (assert) {
|
test('cannot identify target with terminated sessions', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
instances.session.update({ status: 'terminated' });
|
instances.session.update({ status: 'terminated' });
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
assert.notOk(find('.rose-layout-page-header .hds-badge--color-success'));
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
assert.notOk(find('.rose-layout-page-header .hds-badge--color-success'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cancelling a session', async function (assert) {
|
test('cancelling a session', async function (assert) {
|
||||||
assert.expect(2);
|
assert.expect(2);
|
||||||
const sessionsCount = this.server.schema.sessions.all().models.length;
|
|
||||||
stubs.ipcService.withArgs('cancel');
|
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||||
later(async () => {
|
stubs.ipcService.withArgs('connect').returns({
|
||||||
_cancelTimers();
|
session_id: instances.session.id,
|
||||||
await click('tbody tr:first-child td:last-child button');
|
address: 'a_123',
|
||||||
assert.ok(find('[role="alert"].is-success'));
|
port: 'p_123',
|
||||||
assert.strictEqual(findAll('tbody tr').length, sessionsCount - 1);
|
protocol: 'tcp',
|
||||||
}, 750);
|
});
|
||||||
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
|
confirmService.enabled = true;
|
||||||
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
||||||
|
await click('.rose-dialog-footer .rose-button-secondary');
|
||||||
|
const sessionsCount = findAll(
|
||||||
|
'tbody tr:first-child td:last-child button'
|
||||||
|
).length;
|
||||||
|
await click('tbody tr:first-child td:last-child button');
|
||||||
|
assert.ok(find('[role="alert"].is-success'));
|
||||||
|
assert.strictEqual(findAll('tbody tr').length, sessionsCount - 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cancelling a session with error shows notification', async function (assert) {
|
test('cancelling a session with error shows notification', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
|
|
||||||
|
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||||
|
stubs.ipcService.withArgs('connect').returns({
|
||||||
|
session_id: instances.session.id,
|
||||||
|
address: 'a_123',
|
||||||
|
port: 'p_123',
|
||||||
|
protocol: 'tcp',
|
||||||
|
});
|
||||||
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
|
confirmService.enabled = true;
|
||||||
|
|
||||||
this.server.post('/sessions/:id_method', () => new Response(400));
|
this.server.post('/sessions/:id_method', () => new Response(400));
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click('tbody tr:first-child td:last-child button');
|
|
||||||
assert.ok(find('[role="alert"].is-error'));
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
||||||
|
await click('.rose-dialog-footer .rose-button-secondary');
|
||||||
|
await click('tbody tr:first-child td:last-child button');
|
||||||
|
assert.ok(find('[role="alert"].is-error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('cancelling a session with ipc error shows notification', async function (assert) {
|
test('cancelling a session with ipc error shows notification', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
|
|
||||||
|
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||||
|
stubs.ipcService.withArgs('connect').returns({
|
||||||
|
session_id: instances.session.id,
|
||||||
|
address: 'a_123',
|
||||||
|
port: 'p_123',
|
||||||
|
protocol: 'tcp',
|
||||||
|
});
|
||||||
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
|
confirmService.enabled = true;
|
||||||
|
|
||||||
stubs.ipcService.withArgs('stop').throws();
|
stubs.ipcService.withArgs('stop').throws();
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click('tbody tr:first-child td:last-child button');
|
|
||||||
assert.ok(find('[role="alert"].is-error'));
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
||||||
|
await click('.rose-dialog-footer .rose-button-secondary');
|
||||||
|
await click('tbody tr:first-child td:last-child button');
|
||||||
|
assert.ok(find('[role="alert"].is-error'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('connecting to a target', async function (assert) {
|
test('connecting to a target', async function (assert) {
|
||||||
@@ -288,21 +320,18 @@ module('Acceptance | projects | targets | sessions', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
|
||||||
assert.ok(find('.dialog-detail'), 'Success dialog');
|
|
||||||
assert.strictEqual(
|
|
||||||
find('.rose-dialog-footer .rose-button-secondary').textContent.trim(),
|
|
||||||
'Close',
|
|
||||||
'Cannot retry'
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
|
||||||
find('.rose-dialog-body .copyable-content').textContent.trim(),
|
|
||||||
'a_123:p_123'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
||||||
|
assert.ok(find('.dialog-detail'), 'Success dialog');
|
||||||
|
assert.strictEqual(
|
||||||
|
find('.rose-dialog-footer .rose-button-secondary').textContent.trim(),
|
||||||
|
'Close',
|
||||||
|
'Cannot retry'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
find('.rose-dialog-body .copyable-content').textContent.trim(),
|
||||||
|
'a_123:p_123'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('connect details in session', async function (assert) {
|
test('connect details in session', async function (assert) {
|
||||||
@@ -317,20 +346,17 @@ module('Acceptance | projects | targets | sessions', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
instances.session.update('status', 'active');
|
|
||||||
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
|
||||||
assert.ok(find('.dialog-detail'), 'Success dialog');
|
|
||||||
await click('.rose-dialog-dismiss');
|
|
||||||
assert.strictEqual(
|
|
||||||
find(
|
|
||||||
'tbody tr:first-child td:nth-child(2) .copyable-content'
|
|
||||||
).textContent.trim(),
|
|
||||||
'a_123:p_123'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
instances.session.update('status', 'active');
|
||||||
|
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
||||||
|
assert.ok(find('.dialog-detail'), 'Success dialog');
|
||||||
|
await click('.rose-dialog-dismiss');
|
||||||
|
assert.strictEqual(
|
||||||
|
find(
|
||||||
|
'tbody tr:first-child td:nth-child(2) .copyable-content'
|
||||||
|
).textContent.trim(),
|
||||||
|
'a_123:p_123'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles cli error on connect', async function (assert) {
|
test('handles cli error on connect', async function (assert) {
|
||||||
@@ -339,24 +365,21 @@ module('Acceptance | projects | targets | sessions', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
|
||||||
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
|
||||||
const dialogButtons = findAll('.rose-dialog-footer button');
|
|
||||||
assert.strictEqual(dialogButtons.length, 2);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[0].textContent.trim(),
|
|
||||||
'Retry',
|
|
||||||
'Can retry'
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[1].textContent.trim(),
|
|
||||||
'Cancel',
|
|
||||||
'Can cancel'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
||||||
|
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
||||||
|
const dialogButtons = findAll('.rose-dialog-footer button');
|
||||||
|
assert.strictEqual(dialogButtons.length, 2);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[0].textContent.trim(),
|
||||||
|
'Retry',
|
||||||
|
'Can retry'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[1].textContent.trim(),
|
||||||
|
'Cancel',
|
||||||
|
'Can cancel'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles connect error', async function (assert) {
|
test('handles connect error', async function (assert) {
|
||||||
@@ -366,24 +389,21 @@ module('Acceptance | projects | targets | sessions', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
|
||||||
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
|
||||||
const dialogButtons = findAll('.rose-dialog-footer button');
|
|
||||||
assert.strictEqual(dialogButtons.length, 2);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[0].textContent.trim(),
|
|
||||||
'Retry',
|
|
||||||
'Can retry'
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[1].textContent.trim(),
|
|
||||||
'Cancel',
|
|
||||||
'Can cancel'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
||||||
|
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
||||||
|
const dialogButtons = findAll('.rose-dialog-footer button');
|
||||||
|
assert.strictEqual(dialogButtons.length, 2);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[0].textContent.trim(),
|
||||||
|
'Retry',
|
||||||
|
'Can retry'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[1].textContent.trim(),
|
||||||
|
'Cancel',
|
||||||
|
'Can cancel'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can retry on error', async function (assert) {
|
test('can retry on error', async function (assert) {
|
||||||
@@ -392,14 +412,11 @@ module('Acceptance | projects | targets | sessions', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
|
||||||
const firstErrorDialog = find('.rose-dialog');
|
|
||||||
await click('.rose-dialog footer .rose-button-primary', 'Retry');
|
|
||||||
const secondErrorDialog = find('.rose-dialog');
|
|
||||||
assert.notEqual(secondErrorDialog.id, firstErrorDialog.id);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.sessions);
|
await visit(urls.sessions);
|
||||||
|
await click('.rose-layout-page-actions button', 'Activate connect mode');
|
||||||
|
const firstErrorDialog = find('.rose-dialog');
|
||||||
|
await click('.rose-dialog footer .rose-button-primary', 'Retry');
|
||||||
|
const secondErrorDialog = find('.rose-dialog');
|
||||||
|
assert.notEqual(secondErrorDialog.id, firstErrorDialog.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { visit, currentURL, click, find, findAll } from '@ember/test-helpers';
|
import { visit, currentURL, click, find, findAll } from '@ember/test-helpers';
|
||||||
import { later, _cancelTimers } from '@ember/runloop';
|
|
||||||
import { setupApplicationTest } from 'ember-qunit';
|
import { setupApplicationTest } from 'ember-qunit';
|
||||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||||
import { Response } from 'miragejs';
|
import { Response } from 'miragejs';
|
||||||
@@ -173,28 +172,17 @@ module('Acceptance | scopes', function (hooks) {
|
|||||||
test('visiting index', async function (assert) {
|
test('visiting index', async function (assert) {
|
||||||
assert.expect(2);
|
assert.expect(2);
|
||||||
const targetsCount = this.server.schema.targets.all().models.length;
|
const targetsCount = this.server.schema.targets.all().models.length;
|
||||||
// This later/cancelTimers technique allows us to test a page with
|
|
||||||
// active polling. Normally an acceptance test waits for all runloop timers
|
|
||||||
// to stop before returning from an awaited test, but polling means that
|
|
||||||
// runloop timers exist indefinitely. We thus schedule a cancelation before
|
|
||||||
// proceeding with our tests.
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await a11yAudit();
|
|
||||||
assert.strictEqual(currentURL(), urls.targets);
|
|
||||||
assert.strictEqual(findAll('tbody tr').length, targetsCount);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.targets);
|
await visit(urls.targets);
|
||||||
|
assert.strictEqual(currentURL(), urls.targets);
|
||||||
|
assert.strictEqual(findAll('tbody tr').length, targetsCount);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visiting global scope', async function (assert) {
|
test('visiting global scope', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await a11yAudit();
|
|
||||||
assert.strictEqual(currentURL(), urls.globalTargets);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.scopes.global);
|
await visit(urls.scopes.global);
|
||||||
|
await a11yAudit();
|
||||||
|
assert.strictEqual(currentURL(), urls.globalTargets);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: this probably shouldn't be the case, but was setup to enable
|
// TODO: this probably shouldn't be the case, but was setup to enable
|
||||||
@@ -208,43 +196,28 @@ module('Acceptance | scopes', function (hooks) {
|
|||||||
const response = id === 'global' ? new Response(404) : scope;
|
const response = id === 'global' ? new Response(404) : scope;
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await a11yAudit();
|
|
||||||
assert.strictEqual(currentURL(), urls.globalTargets);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.scopes.global);
|
await visit(urls.scopes.global);
|
||||||
|
await a11yAudit();
|
||||||
|
assert.strictEqual(currentURL(), urls.globalTargets);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visiting org scope', async function (assert) {
|
test('visiting org scope', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await a11yAudit();
|
|
||||||
assert.strictEqual(currentURL(), urls.targets);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.scopes.org);
|
await visit(urls.scopes.org);
|
||||||
|
await a11yAudit();
|
||||||
|
assert.strictEqual(currentURL(), urls.targets);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('can navigate among org scopes via header navigation', async function (assert) {
|
test('can navigate among org scopes via header navigation', async function (assert) {
|
||||||
assert.expect(3);
|
assert.expect(3);
|
||||||
await later(async () => _cancelTimers(), 750);
|
|
||||||
await visit(urls.targets);
|
await visit(urls.targets);
|
||||||
await later(
|
|
||||||
async () => assert.strictEqual(currentURL(), urls.targets),
|
|
||||||
750
|
|
||||||
);
|
|
||||||
await click('.rose-header-nav .rose-dropdown a:nth-of-type(2)');
|
await click('.rose-header-nav .rose-dropdown a:nth-of-type(2)');
|
||||||
await later(
|
assert.strictEqual(currentURL(), urls.targets);
|
||||||
async () => assert.strictEqual(currentURL(), urls.org2Targets),
|
|
||||||
750
|
|
||||||
);
|
|
||||||
await click('.rose-header-nav .rose-dropdown a:nth-of-type(3)');
|
await click('.rose-header-nav .rose-dropdown a:nth-of-type(3)');
|
||||||
later(
|
assert.strictEqual(currentURL(), urls.org2Targets);
|
||||||
async () => assert.strictEqual(currentURL(), urls.globalTargets),
|
|
||||||
750
|
|
||||||
);
|
|
||||||
await click('.rose-header-nav .rose-dropdown a:nth-of-type(1)');
|
await click('.rose-header-nav .rose-dropdown a:nth-of-type(1)');
|
||||||
|
assert.strictEqual(currentURL(), urls.globalTargets);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
|
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
|
||||||
@@ -258,25 +231,19 @@ module('Acceptance | scopes', function (hooks) {
|
|||||||
|
|
||||||
test('visiting a target', async function (assert) {
|
test('visiting a target', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click('tbody tr th a');
|
|
||||||
assert.strictEqual(currentURL(), urls.targetSessions);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.targets);
|
await visit(urls.targets);
|
||||||
|
await click('tbody tr th a');
|
||||||
|
assert.strictEqual(currentURL(), urls.targetSessions);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('visiting empty targets', async function (assert) {
|
test('visiting empty targets', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
this.server.get('/targets', () => new Response(200));
|
this.server.get('/targets', () => new Response(200));
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
assert.ok(
|
|
||||||
find('.rose-message-title').textContent.trim(),
|
|
||||||
'No Targets Available'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.targets);
|
await visit(urls.targets);
|
||||||
|
assert.ok(
|
||||||
|
find('.rose-message-title').textContent.trim(),
|
||||||
|
'No Targets Available'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('connecting to a target', async function (assert) {
|
test('connecting to a target', async function (assert) {
|
||||||
@@ -291,21 +258,18 @@ module('Acceptance | scopes', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click(
|
|
||||||
'tbody tr:first-child td:last-child button',
|
|
||||||
'Activate connect mode'
|
|
||||||
);
|
|
||||||
assert.ok(find('.dialog-detail'), 'Success dialog');
|
|
||||||
assert.strictEqual(findAll('.rose-dialog-footer button').length, 1);
|
|
||||||
assert.strictEqual(
|
|
||||||
find('.rose-dialog-footer button').textContent.trim(),
|
|
||||||
'Close',
|
|
||||||
'Cannot retry'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.targets);
|
await visit(urls.targets);
|
||||||
|
await click(
|
||||||
|
'tbody tr:first-child td:last-child button',
|
||||||
|
'Activate connect mode'
|
||||||
|
);
|
||||||
|
assert.ok(find('.dialog-detail'), 'Success dialog');
|
||||||
|
assert.strictEqual(findAll('.rose-dialog-footer button').length, 1);
|
||||||
|
assert.strictEqual(
|
||||||
|
find('.rose-dialog-footer button').textContent.trim(),
|
||||||
|
'Close',
|
||||||
|
'Cannot retry'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles cli error on connect', async function (assert) {
|
test('handles cli error on connect', async function (assert) {
|
||||||
@@ -314,27 +278,24 @@ module('Acceptance | scopes', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click(
|
|
||||||
'tbody tr:first-child td:last-child button',
|
|
||||||
'Activate connect mode'
|
|
||||||
);
|
|
||||||
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
|
||||||
const dialogButtons = findAll('.rose-dialog-footer button');
|
|
||||||
assert.strictEqual(dialogButtons.length, 2);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[0].textContent.trim(),
|
|
||||||
'Retry',
|
|
||||||
'Can retry'
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[1].textContent.trim(),
|
|
||||||
'Cancel',
|
|
||||||
'Can cancel'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.scopes.global);
|
await visit(urls.scopes.global);
|
||||||
|
await click(
|
||||||
|
'tbody tr:first-child td:last-child button',
|
||||||
|
'Activate connect mode'
|
||||||
|
);
|
||||||
|
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
||||||
|
const dialogButtons = findAll('.rose-dialog-footer button');
|
||||||
|
assert.strictEqual(dialogButtons.length, 2);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[0].textContent.trim(),
|
||||||
|
'Retry',
|
||||||
|
'Can retry'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[1].textContent.trim(),
|
||||||
|
'Cancel',
|
||||||
|
'Can cancel'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('handles connect error', async function (assert) {
|
test('handles connect error', async function (assert) {
|
||||||
@@ -344,26 +305,23 @@ module('Acceptance | scopes', function (hooks) {
|
|||||||
const confirmService = this.owner.lookup('service:confirm');
|
const confirmService = this.owner.lookup('service:confirm');
|
||||||
confirmService.enabled = true;
|
confirmService.enabled = true;
|
||||||
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await click(
|
|
||||||
'tbody tr:first-child td:last-child button',
|
|
||||||
'Activate connect mode'
|
|
||||||
);
|
|
||||||
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
|
||||||
const dialogButtons = findAll('.rose-dialog-footer button');
|
|
||||||
assert.strictEqual(dialogButtons.length, 2);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[0].textContent.trim(),
|
|
||||||
'Retry',
|
|
||||||
'Can retry'
|
|
||||||
);
|
|
||||||
assert.strictEqual(
|
|
||||||
dialogButtons[1].textContent.trim(),
|
|
||||||
'Cancel',
|
|
||||||
'Can cancel'
|
|
||||||
);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.targets);
|
await visit(urls.targets);
|
||||||
|
await click(
|
||||||
|
'tbody tr:first-child td:last-child button',
|
||||||
|
'Activate connect mode'
|
||||||
|
);
|
||||||
|
assert.ok(find('.rose-dialog-error'), 'Error dialog');
|
||||||
|
const dialogButtons = findAll('.rose-dialog-footer button');
|
||||||
|
assert.strictEqual(dialogButtons.length, 2);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[0].textContent.trim(),
|
||||||
|
'Retry',
|
||||||
|
'Can retry'
|
||||||
|
);
|
||||||
|
assert.strictEqual(
|
||||||
|
dialogButtons[1].textContent.trim(),
|
||||||
|
'Cancel',
|
||||||
|
'Can cancel'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
import { module, test } from 'qunit';
|
import { module, test } from 'qunit';
|
||||||
import { visit, currentURL } from '@ember/test-helpers';
|
import { visit, currentURL } from '@ember/test-helpers';
|
||||||
import { later, _cancelTimers } from '@ember/runloop';
|
|
||||||
import { setupApplicationTest } from 'ember-qunit';
|
import { setupApplicationTest } from 'ember-qunit';
|
||||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||||
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
||||||
@@ -113,16 +112,7 @@ module('Acceptance | targets', function (hooks) {
|
|||||||
|
|
||||||
test('visiting index', async function (assert) {
|
test('visiting index', async function (assert) {
|
||||||
assert.expect(1);
|
assert.expect(1);
|
||||||
// This later/cancelTimers technique allows us to test a page with
|
|
||||||
// active polling. Normally an acceptance test waits for all runloop timers
|
|
||||||
// to stop before returning from an awaited test, but polling means that
|
|
||||||
// runloop timers exist indefinitely. We thus schedule a cancellation before
|
|
||||||
// proceeding with our tests.
|
|
||||||
later(async () => {
|
|
||||||
_cancelTimers();
|
|
||||||
await a11yAudit();
|
|
||||||
assert.strictEqual(currentURL(), urls.targets);
|
|
||||||
}, 750);
|
|
||||||
await visit(urls.targets);
|
await visit(urls.targets);
|
||||||
|
assert.strictEqual(currentURL(), urls.targets);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user