feat: 🎸 Add pagination component to targets (#2023)
* feat: 🎸 Add pagination component to targets * test: 💍 Fix tests and add helper for client daemon search * refactor: 💡 Update the returns from client daemon ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-11905
This commit is contained in:
1 parent
dfd484dbca
commit
15d815c271
16 files changed
+253
-181
No files matched your search
@@ -0,0 +1,62 @@
|
||||
import sinon from 'sinon';
|
||||
|
||||
/**
|
||||
* This test helper can be used to help setup your sinon stubs in your tests.
|
||||
* Must be called after setupMirage.
|
||||
*
|
||||
* ```js
|
||||
* import setupStubs from 'api/test-support/handlers/client-daemon-search';
|
||||
*
|
||||
* module('Acceptance | my test', function(hooks) {
|
||||
* setupApplicationTest(hooks);
|
||||
* setupMirage(hooks);
|
||||
* setupStubs(hooks);
|
||||
*
|
||||
* // add your actual tests here
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* This will setup a stub for the IPC service and include helper methods to
|
||||
* help setup what the client daemon search command returns.
|
||||
*
|
||||
* @module Test Helpers
|
||||
* @public
|
||||
*/
|
||||
export default function setupStubs(hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
const ipcService = this.owner.lookup('service:ipc');
|
||||
this.ipcStub = sinon.stub(ipcService, 'invoke');
|
||||
|
||||
/**
|
||||
* We aim to still use mirage data when stubbing out the client daemon
|
||||
* search. However, the handler expects the search data to be raw JSON from
|
||||
* the API which will get serialized. We add back the scope object for it to
|
||||
* get correctly normalized and other fields such as "attributes" from the
|
||||
* API will already be hoisted and will not be re-normalized from mirage data.
|
||||
*
|
||||
* This convenience method can take in multiple types,
|
||||
* e.g. this.stubClientDaemonSearch('targets', 'sessions').
|
||||
* This will have sinon stub out each call so the first call to search will
|
||||
* provide all the models from the first parameter you specify,
|
||||
* the second call will provide all the models from the second argument, etc.
|
||||
* @param types
|
||||
*/
|
||||
this.stubClientDaemonSearch = (...types) => {
|
||||
types.forEach((type, i) => {
|
||||
this.ipcStub
|
||||
.withArgs('searchClientDaemon')
|
||||
.onCall(i)
|
||||
.returns({
|
||||
[type]: this.server.db[type].map((model) => ({
|
||||
...model,
|
||||
scope: this.server.db.scopes.find(model.scopeId),
|
||||
})),
|
||||
});
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
sinon.restore();
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { inject as service } from '@ember/service';
|
||||
import { getOwner, setOwner } from '@ember/application';
|
||||
import ArrayProxy from '@ember/array/proxy';
|
||||
import { pluralize } from 'ember-inflector';
|
||||
import { generateMQLExpression } from '../utils/mqlQuery';
|
||||
|
||||
@@ -111,17 +110,20 @@ export default class ClientDaemonHandler {
|
||||
);
|
||||
|
||||
// Return the raw data if we don't push to the store.
|
||||
let records = normalizedPayload.data;
|
||||
let records = normalizedPayload.data.map((record) => ({
|
||||
...record.attributes,
|
||||
id: record.id,
|
||||
}));
|
||||
if (pushToStore) {
|
||||
records = store.push(normalizedPayload);
|
||||
}
|
||||
|
||||
// Use a proxy array to store the metadata so the store's `query`
|
||||
// array requirement is satisfied.
|
||||
return ArrayProxy.create({
|
||||
content: records,
|
||||
meta: { totalItems: results?.length ?? 0 },
|
||||
});
|
||||
// Set a meta property on the array to store the total items of the results.
|
||||
// This isn't conventional but is better than returning an ArrayProxy
|
||||
// or EmberArray since the ember store query method asserts it has to be an array
|
||||
// so we can't just return an object.
|
||||
records.meta = { totalItems: results?.length ?? 0 };
|
||||
return records;
|
||||
}
|
||||
default:
|
||||
return next(context.request);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<Hds::Pagination::Numbered
|
||||
@totalItems={{@totalItems}}
|
||||
@currentPage={{@currentPage}}
|
||||
@currentPageSize={{@currentPageSize}}
|
||||
@route={{this.router.currentRouteName}}
|
||||
@queryFunction={{this.paginationQueryParams}}
|
||||
/>
|
||||
@@ -0,0 +1,19 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
|
||||
export default class RosePaginationComponent extends Component {
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* Returns the query params needed for the pagination component.
|
||||
* @returns {function(number, number): {pageSize: number, page: number}}
|
||||
*/
|
||||
get paginationQueryParams() {
|
||||
return (page, pageSize) => {
|
||||
return {
|
||||
page,
|
||||
pageSize,
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'rose/components/rose/pagination';
|
||||
@@ -21,9 +21,11 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle
|
||||
|
||||
// =attributes
|
||||
|
||||
queryParams = ['search'];
|
||||
queryParams = ['search', 'page', 'pageSize'];
|
||||
|
||||
@tracked search;
|
||||
@tracked page = 1;
|
||||
@tracked pageSize = 10;
|
||||
@tracked searchItems = ['Project 1', 'Project 2', 'Project 3'];
|
||||
|
||||
// =methods
|
||||
@@ -138,5 +140,6 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle
|
||||
handleSearchInput(event) {
|
||||
const { value } = event.target;
|
||||
this.search = value;
|
||||
this.page = 1;
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export default class ScopesScopeProjectsSessionsRoute extends Route {
|
||||
this.router.currentRoute.name ===
|
||||
'scopes.scope.projects.sessions.session.index'
|
||||
) {
|
||||
await this.router.replaceWith('scopes.scope.projects.targets');
|
||||
this.router.replaceWith('scopes.scope.projects.targets');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,12 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route {
|
||||
refreshModel: true,
|
||||
replace: true,
|
||||
},
|
||||
page: {
|
||||
refreshModel: true,
|
||||
},
|
||||
pageSize: {
|
||||
refreshModel: true,
|
||||
},
|
||||
};
|
||||
|
||||
// =methods
|
||||
@@ -39,10 +45,15 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route {
|
||||
*
|
||||
* @returns {Promise<{totalItems: number, targets: [TargetModel]}>}
|
||||
*/
|
||||
async model({ search = '' }) {
|
||||
async model({ search, page, pageSize }) {
|
||||
// TODO: Filter targets by scope we're in manually
|
||||
// const { id: scope_id } = this.modelFor('scopes.scope');
|
||||
const queryOptions = { query: { search } };
|
||||
|
||||
const queryOptions = {
|
||||
query: { search },
|
||||
page,
|
||||
pageSize,
|
||||
};
|
||||
|
||||
let targets = await this.store.query('target', queryOptions);
|
||||
const totalItems = targets.meta?.totalItems;
|
||||
|
||||
@@ -686,16 +686,20 @@ $chevron-down: url("data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%231
|
||||
}
|
||||
|
||||
// Search & Filtering
|
||||
.targets {
|
||||
.search-filtering {
|
||||
.hds-application-state {
|
||||
padding-top: 3rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.target-search-filtering {
|
||||
padding-bottom: 1rem;
|
||||
.hds-segmented-group {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
.hds-dropdown-toggle-button__text {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.hds-table {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
@@ -13,47 +13,45 @@
|
||||
<p>{{t 'resources.target.description'}}</p>
|
||||
</page.header>
|
||||
|
||||
<page.body class='targets'>
|
||||
<page.body class='search-filtering'>
|
||||
{{#if (or @model.targets this.search)}}
|
||||
<div class='target-search-filtering'>
|
||||
<Hds::SegmentedGroup as |S|>
|
||||
<S.TextInput
|
||||
@type='search'
|
||||
placeholder={{t 'actions.search'}}
|
||||
aria-label={{t 'actions.search'}}
|
||||
{{on 'input' this.handleSearchInput}}
|
||||
<Hds::SegmentedGroup as |S|>
|
||||
<S.TextInput
|
||||
@type='search'
|
||||
placeholder={{t 'actions.search'}}
|
||||
aria-label={{t 'actions.search'}}
|
||||
{{on 'input' this.handleSearchInput}}
|
||||
/>
|
||||
<S.Dropdown as |DD|>
|
||||
<DD.ToggleButton
|
||||
@color='secondary'
|
||||
@text={{t 'resources.scope.title'}}
|
||||
/>
|
||||
<S.Dropdown as |DD|>
|
||||
<DD.ToggleButton
|
||||
@color='secondary'
|
||||
@text={{t 'resources.scope.title'}}
|
||||
<DD.Header @hasDivider={{true}}>
|
||||
<Hds::Form::TextInput::Base
|
||||
@type='search'
|
||||
placeholder={{t 'actions.narrow-results'}}
|
||||
aria-label={{t 'actions.narrow-results'}}
|
||||
data-test-search
|
||||
/>
|
||||
<DD.Header @hasDivider={{true}}>
|
||||
<Hds::Form::TextInput::Base
|
||||
@type='search'
|
||||
placeholder={{t 'actions.narrow-results'}}
|
||||
aria-label={{t 'actions.narrow-results'}}
|
||||
data-test-search
|
||||
/>
|
||||
</DD.Header>
|
||||
{{#each this.searchItems as |item|}}
|
||||
<DD.Checkbox @value={{item}} data-test-checkbox={{item}}>
|
||||
{{item}}
|
||||
</DD.Checkbox>
|
||||
{{/each}}
|
||||
</S.Dropdown>
|
||||
<S.Dropdown as |DD|>
|
||||
<DD.ToggleButton @color='secondary' @text='Active session' />
|
||||
<DD.Checkbox>{{t 'actions.yes'}}</DD.Checkbox>
|
||||
<DD.Checkbox>{{t 'actions.no'}}</DD.Checkbox>
|
||||
</S.Dropdown>
|
||||
<S.Dropdown as |DD|>
|
||||
<DD.ToggleButton @color='secondary' @text='Type' @count='2' />
|
||||
<DD.Checkbox>{{t 'resources.target.types.tcp'}}</DD.Checkbox>
|
||||
<DD.Checkbox>{{t 'resources.target.types.ssh'}}</DD.Checkbox>
|
||||
</S.Dropdown>
|
||||
</Hds::SegmentedGroup>
|
||||
</div>
|
||||
</DD.Header>
|
||||
{{#each this.searchItems as |item|}}
|
||||
<DD.Checkbox @value={{item}} data-test-checkbox={{item}}>
|
||||
{{item}}
|
||||
</DD.Checkbox>
|
||||
{{/each}}
|
||||
</S.Dropdown>
|
||||
<S.Dropdown as |DD|>
|
||||
<DD.ToggleButton @color='secondary' @text='Active session' />
|
||||
<DD.Checkbox>{{t 'actions.yes'}}</DD.Checkbox>
|
||||
<DD.Checkbox>{{t 'actions.no'}}</DD.Checkbox>
|
||||
</S.Dropdown>
|
||||
<S.Dropdown as |DD|>
|
||||
<DD.ToggleButton @color='secondary' @text='Type' @count='2' />
|
||||
<DD.Checkbox>{{t 'resources.target.types.tcp'}}</DD.Checkbox>
|
||||
<DD.Checkbox>{{t 'resources.target.types.ssh'}}</DD.Checkbox>
|
||||
</S.Dropdown>
|
||||
</Hds::SegmentedGroup>
|
||||
{{#if @model.targets}}
|
||||
<Hds::Table
|
||||
@model={{@model.targets}}
|
||||
@@ -139,6 +137,12 @@
|
||||
</B.Tr>
|
||||
</:body>
|
||||
</Hds::Table>
|
||||
<Rose::Pagination
|
||||
@totalItems={{@model.totalItems}}
|
||||
@currentPage={{this.page}}
|
||||
@currentPageSize={{this.pageSize}}
|
||||
/>
|
||||
|
||||
{{/if}}
|
||||
{{#if this.noResults}}
|
||||
<Hds::ApplicationState data-test-no-target-results as |A|>
|
||||
|
||||
@@ -146,7 +146,7 @@ handle('searchClientDaemon', async (request) =>
|
||||
* Check to see if the client daemon is running. We use the presence of a
|
||||
* socket path as a proxy for whether the daemon is running.
|
||||
*/
|
||||
handle('isClientDaemonRunning', async (request) =>
|
||||
handle('isClientDaemonRunning', async () =>
|
||||
Boolean(clientDaemonManager.socketPath),
|
||||
);
|
||||
|
||||
|
||||
@@ -15,17 +15,18 @@ import {
|
||||
authenticateSession,
|
||||
invalidateSession,
|
||||
} from 'ember-simple-auth/test-support';
|
||||
import sinon from 'sinon';
|
||||
import {
|
||||
STATUS_SESSION_ACTIVE,
|
||||
STATUS_SESSION_PENDING,
|
||||
STATUS_SESSION_CANCELING,
|
||||
STATUS_SESSION_TERMINATED,
|
||||
} from 'api/models/session';
|
||||
import setupStubs from 'api/test-support/handlers/client-daemon-search';
|
||||
|
||||
module('Acceptance | projects | sessions', function (hooks) {
|
||||
module('Acceptance | projects | sessions | index', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupStubs(hooks);
|
||||
|
||||
const instances = {
|
||||
scopes: {
|
||||
@@ -40,12 +41,6 @@ module('Acceptance | projects | sessions', function (hooks) {
|
||||
session: null,
|
||||
};
|
||||
|
||||
const stubs = {
|
||||
global: null,
|
||||
org: null,
|
||||
ipcService: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -77,17 +72,16 @@ module('Acceptance | projects | sessions', function (hooks) {
|
||||
|
||||
// create scopes
|
||||
instances.scopes.global = this.server.create('scope', { id: 'global' });
|
||||
stubs.global = { id: 'global', type: 'global' };
|
||||
const globalScope = { id: 'global', type: 'global' };
|
||||
instances.scopes.org = this.server.create('scope', {
|
||||
type: 'org',
|
||||
scope: stubs.global,
|
||||
scope: globalScope,
|
||||
});
|
||||
stubs.org = { id: instances.scopes.org.id, type: 'org' };
|
||||
const orgScope = { id: instances.scopes.org.id, type: 'org' };
|
||||
instances.scopes.project = this.server.create('scope', {
|
||||
type: 'project',
|
||||
scope: stubs.org,
|
||||
scope: orgScope,
|
||||
});
|
||||
stubs.project = { id: instances.scopes.project.id, type: 'project' };
|
||||
|
||||
instances.authMethods.global = this.server.create('auth-method', {
|
||||
scope: instances.scopes.global,
|
||||
@@ -120,12 +114,7 @@ module('Acceptance | projects | sessions', function (hooks) {
|
||||
this.owner.register('service:browser/window', WindowMockIPC);
|
||||
setDefaultClusterUrl(this);
|
||||
|
||||
const ipcService = this.owner.lookup('service:ipc');
|
||||
stubs.ipcService = sinon.stub(ipcService, 'invoke');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
sinon.restore();
|
||||
this.ipcStub.withArgs('isClientDaemonRunning').returns(false);
|
||||
});
|
||||
|
||||
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
|
||||
@@ -299,7 +288,7 @@ module('Acceptance | projects | sessions', function (hooks) {
|
||||
|
||||
test('cancelling a session shows success alert', async function (assert) {
|
||||
assert.expect(1);
|
||||
stubs.ipcService.withArgs('stop');
|
||||
this.ipcStub.withArgs('stop');
|
||||
|
||||
await visit(urls.sessions);
|
||||
await click('tbody tr:first-child td:last-child button');
|
||||
@@ -309,7 +298,7 @@ module('Acceptance | projects | sessions', function (hooks) {
|
||||
|
||||
test('cancelling a session keeps you on the sessions list screen', async function (assert) {
|
||||
assert.expect(1);
|
||||
stubs.ipcService.withArgs('stop');
|
||||
this.ipcStub.withArgs('stop');
|
||||
|
||||
await visit(urls.sessions);
|
||||
await click('tbody tr:first-child td:last-child button');
|
||||
@@ -329,7 +318,7 @@ module('Acceptance | projects | sessions', function (hooks) {
|
||||
|
||||
test('cancelling a session with ipc error shows notification', async function (assert) {
|
||||
assert.expect(1);
|
||||
stubs.ipcService.withArgs('stop').throws();
|
||||
this.ipcStub.withArgs('stop').throws();
|
||||
|
||||
await visit(urls.sessions);
|
||||
await click('tbody tr:first-child td:last-child button');
|
||||
|
||||
@@ -12,13 +12,14 @@ import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { Response } from 'miragejs';
|
||||
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import sinon from 'sinon';
|
||||
import WindowMockIPC from '../../../helpers/window-mock-ipc';
|
||||
import { STATUS_SESSION_ACTIVE } from 'api/models/session';
|
||||
import setupStubs from 'api/test-support/handlers/client-daemon-search';
|
||||
|
||||
module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupStubs(hooks);
|
||||
|
||||
const TARGET_CONNECT_BUTTON = '[data-test-target-detail-connect-button]';
|
||||
|
||||
@@ -35,12 +36,6 @@ module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
session: null,
|
||||
};
|
||||
|
||||
const stubs = {
|
||||
global: null,
|
||||
org: null,
|
||||
ipcService: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -70,17 +65,17 @@ module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
|
||||
// create scopes
|
||||
instances.scopes.global = this.server.create('scope', { id: 'global' });
|
||||
stubs.global = { id: 'global', type: 'global' };
|
||||
const globalScope = { id: 'global', type: 'global' };
|
||||
instances.scopes.org = this.server.create('scope', {
|
||||
type: 'org',
|
||||
scope: stubs.global,
|
||||
scope: globalScope,
|
||||
});
|
||||
stubs.org = { id: instances.scopes.org.id, type: 'org' };
|
||||
const orgScope = { id: instances.scopes.org.id, type: 'org' };
|
||||
instances.scopes.project = this.server.create('scope', {
|
||||
type: 'project',
|
||||
scope: stubs.org,
|
||||
scope: orgScope,
|
||||
});
|
||||
stubs.project = { id: instances.scopes.project.id, type: 'project' };
|
||||
|
||||
instances.hostCatalog = this.server.create('host-catalog', {
|
||||
scope: instances.scopes.project,
|
||||
});
|
||||
@@ -119,12 +114,10 @@ module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
this.owner.register('service:browser/window', WindowMockIPC);
|
||||
setDefaultClusterUrl(this);
|
||||
|
||||
const ipcService = this.owner.lookup('service:ipc');
|
||||
stubs.ipcService = sinon.stub(ipcService, 'invoke');
|
||||
this.ipcStub.withArgs('isClientDaemonRunning').returns(false);
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
sinon.restore();
|
||||
// reset onUncaughtException to original state
|
||||
QUnit.onUncaughtException = originalUncaughtException;
|
||||
});
|
||||
@@ -140,8 +133,8 @@ module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
|
||||
test('visiting session with no credentials', async function (assert) {
|
||||
assert.expect(4);
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').returns({
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').returns({
|
||||
session_id: instances.session.id,
|
||||
address: 'a_123',
|
||||
port: 'p_123',
|
||||
@@ -162,8 +155,8 @@ module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
test('visiting a session that does not have permissions to read a host', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.get('/hosts/:id', () => new Response(403));
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').returns({
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').returns({
|
||||
session_id: instances.session.id,
|
||||
host_id: 'h_123',
|
||||
address: 'a_123',
|
||||
@@ -185,8 +178,8 @@ module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
};
|
||||
|
||||
this.server.get('/sessions/:id', () => new Response(403));
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').returns({
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').returns({
|
||||
session_id: instances.session.id,
|
||||
host_id: 'h_123',
|
||||
address: 'a_123',
|
||||
@@ -220,7 +213,6 @@ module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
|
||||
test('cancelling a session shows success alert', async function (assert) {
|
||||
assert.expect(1);
|
||||
stubs.ipcService.withArgs('stop');
|
||||
|
||||
await visit(urls.session);
|
||||
await click('[data-test-session-detail-cancel-button]');
|
||||
@@ -230,7 +222,6 @@ module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
|
||||
test('cancelling a session takes you to the targets list screen', async function (assert) {
|
||||
assert.expect(1);
|
||||
stubs.ipcService.withArgs('stop');
|
||||
|
||||
await visit(urls.session);
|
||||
await click('[data-test-session-detail-cancel-button]');
|
||||
@@ -250,7 +241,7 @@ module('Acceptance | projects | sessions | session', function (hooks) {
|
||||
|
||||
test('cancelling a session with ipc error shows notification', async function (assert) {
|
||||
assert.expect(1);
|
||||
stubs.ipcService.withArgs('stop').throws();
|
||||
this.ipcStub.withArgs('stop').throws();
|
||||
|
||||
await visit(urls.session);
|
||||
await click('[data-test-session-detail-cancel-button]');
|
||||
|
||||
@@ -12,19 +12,19 @@ import {
|
||||
} from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { Response } from 'miragejs';
|
||||
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
||||
import sinon from 'sinon';
|
||||
import WindowMockIPC from '../../../helpers/window-mock-ipc';
|
||||
import {
|
||||
authenticateSession,
|
||||
invalidateSession,
|
||||
currentSession,
|
||||
} from 'ember-simple-auth/test-support';
|
||||
import setupStubs from 'api/test-support/handlers/client-daemon-search';
|
||||
|
||||
module('Acceptance | projects | targets | index', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupStubs(hooks);
|
||||
|
||||
let getTargetCount;
|
||||
|
||||
@@ -51,12 +51,6 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
session: null,
|
||||
};
|
||||
|
||||
const stubs = {
|
||||
global: null,
|
||||
org: null,
|
||||
ipcService: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -87,17 +81,16 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
|
||||
// Generate scopes
|
||||
instances.scopes.global = this.server.create('scope', { id: 'global' });
|
||||
stubs.global = { id: 'global', type: 'global' };
|
||||
const globalScope = { id: 'global', type: 'global' };
|
||||
instances.scopes.org = this.server.create('scope', {
|
||||
type: 'org',
|
||||
scope: stubs.global,
|
||||
scope: globalScope,
|
||||
});
|
||||
stubs.org = { id: instances.scopes.org.id, type: 'org' };
|
||||
const orgScope = { id: instances.scopes.org.id, type: 'org' };
|
||||
instances.scopes.project = this.server.create('scope', {
|
||||
type: 'project',
|
||||
scope: stubs.org,
|
||||
scope: orgScope,
|
||||
});
|
||||
stubs.project = { id: instances.scopes.project.id, type: 'project' };
|
||||
|
||||
// Generate resources
|
||||
instances.authMethods.global = this.server.create('auth-method', {
|
||||
@@ -137,12 +130,8 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
this.owner.register('service:browser/window', WindowMockIPC);
|
||||
setDefaultClusterUrl(this);
|
||||
|
||||
const ipcService = this.owner.lookup('service:ipc');
|
||||
stubs.ipcService = sinon.stub(ipcService, 'invoke');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
sinon.restore();
|
||||
this.ipcStub.withArgs('isClientDaemonRunning').returns(true);
|
||||
this.stubClientDaemonSearch('targets');
|
||||
});
|
||||
|
||||
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
|
||||
@@ -182,7 +171,9 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
|
||||
test('visiting targets list view with no targets', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.get('/targets', () => new Response(200));
|
||||
this.server.db.targets.remove();
|
||||
this.stubClientDaemonSearch('targets');
|
||||
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
@@ -194,6 +185,8 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
assert.expect(1);
|
||||
instances.target.authorized_actions =
|
||||
instances.target.authorized_actions.filter((item) => item !== 'read');
|
||||
this.stubClientDaemonSearch('targets');
|
||||
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
@@ -223,6 +216,8 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
instances.target.authorized_actions.filter(
|
||||
(item) => item !== 'authorize-session',
|
||||
);
|
||||
this.stubClientDaemonSearch('targets');
|
||||
|
||||
await visit(urls.projects);
|
||||
|
||||
await click(`[href="${urls.targets}"]`);
|
||||
@@ -237,8 +232,8 @@ 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);
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').rejects();
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').rejects();
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
|
||||
@@ -255,8 +250,9 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
assert.expect(2);
|
||||
instances.target.authorized_actions =
|
||||
instances.target.authorized_actions.filter((item) => item !== 'read');
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').returns({
|
||||
this.stubClientDaemonSearch('targets');
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').returns({
|
||||
session_id: instances.session.id,
|
||||
address: 'a_123',
|
||||
port: 'p_123',
|
||||
@@ -278,8 +274,9 @@ module('Acceptance | projects | targets | index', function (hooks) {
|
||||
assert.expect(5);
|
||||
instances.target.authorized_actions =
|
||||
instances.target.authorized_actions.filter((item) => item !== 'read');
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').rejects();
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').rejects();
|
||||
this.stubClientDaemonSearch('targets');
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
|
||||
|
||||
@@ -7,13 +7,14 @@ import { module, test } from 'qunit';
|
||||
import { visit, currentURL, click, find } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import sinon from 'sinon';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import WindowMockIPC from '../../../helpers/window-mock-ipc';
|
||||
import setupStubs from 'api/test-support/handlers/client-daemon-search';
|
||||
|
||||
module('Acceptance | projects | targets | target', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupStubs(hooks);
|
||||
|
||||
const TARGET_CONNECT_BUTTON = '[data-test-target-detail-connect-button]';
|
||||
const TARGET_QUICK_CONNECT_BUTTON = '[data-test-host-quick-connect]';
|
||||
@@ -40,12 +41,6 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
targetWithTwoHosts: null,
|
||||
};
|
||||
|
||||
const stubs = {
|
||||
global: null,
|
||||
org: null,
|
||||
ipcService: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
scopes: {
|
||||
global: null,
|
||||
@@ -72,17 +67,16 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
|
||||
// Generate scopes
|
||||
instances.scopes.global = this.server.create('scope', { id: 'global' });
|
||||
stubs.global = { id: 'global', type: 'global' };
|
||||
const globalScope = { id: 'global', type: 'global' };
|
||||
instances.scopes.org = this.server.create('scope', {
|
||||
type: 'org',
|
||||
scope: stubs.global,
|
||||
scope: globalScope,
|
||||
});
|
||||
stubs.org = { id: instances.scopes.org.id, type: 'org' };
|
||||
const orgScope = { id: instances.scopes.org.id, type: 'org' };
|
||||
instances.scopes.project = this.server.create('scope', {
|
||||
type: 'project',
|
||||
scope: stubs.org,
|
||||
scope: orgScope,
|
||||
});
|
||||
stubs.project = { id: instances.scopes.project.id, type: 'project' };
|
||||
|
||||
// Generate resources
|
||||
instances.authMethods.global = this.server.create('auth-method', {
|
||||
@@ -129,18 +123,14 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
this.owner.register('service:browser/window', WindowMockIPC);
|
||||
setDefaultClusterUrl(this);
|
||||
|
||||
const ipcService = this.owner.lookup('service:ipc');
|
||||
stubs.ipcService = sinon.stub(ipcService, 'invoke');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
sinon.restore();
|
||||
this.ipcStub.withArgs('isClientDaemonRunning').returns(true);
|
||||
this.stubClientDaemonSearch('targets');
|
||||
});
|
||||
|
||||
test('user can connect to a target with an address', async function (assert) {
|
||||
assert.expect(3);
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').returns({
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').returns({
|
||||
session_id: instances.session.id,
|
||||
address: 'localhost',
|
||||
port: 'p_123',
|
||||
@@ -159,7 +149,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
|
||||
test('handles cli error on connect', async function (assert) {
|
||||
assert.expect(4);
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
await visit(urls.targets);
|
||||
@@ -175,8 +165,8 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
|
||||
test('handles connect error', async function (assert) {
|
||||
assert.expect(4);
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').rejects();
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').rejects();
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
await visit(urls.targets);
|
||||
@@ -192,7 +182,7 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
|
||||
test('user can retry on error', async function (assert) {
|
||||
assert.expect(1);
|
||||
stubs.ipcService.withArgs('cliExists').rejects();
|
||||
this.ipcStub.withArgs('cliExists').rejects();
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
|
||||
@@ -208,8 +198,8 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
|
||||
test('user can connect to a target with one host', async function (assert) {
|
||||
assert.expect(2);
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').returns({
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').returns({
|
||||
session_id: instances.session.id,
|
||||
address: 'a_123',
|
||||
port: 'p_123',
|
||||
@@ -228,8 +218,8 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
|
||||
test('user can connect to a target with two hosts using host modal', async function (assert) {
|
||||
assert.expect(3);
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').returns({
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').returns({
|
||||
session_id: instances.session.id,
|
||||
address: 'a_123',
|
||||
port: 'p_123',
|
||||
@@ -275,8 +265,8 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
test('user can connect to a target without read permissions for host-set', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.get('/host-sets/:id', () => new Response(403));
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').returns({
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').returns({
|
||||
session_id: instances.session.id,
|
||||
address: 'a_123',
|
||||
port: 'p_123',
|
||||
@@ -294,8 +284,8 @@ module('Acceptance | projects | targets | target', function (hooks) {
|
||||
test('user can connect to a target without read permissions for host', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.get('/hosts/:id', () => new Response(403));
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').returns({
|
||||
this.ipcStub.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.withArgs('connect').returns({
|
||||
session_id: instances.session.id,
|
||||
address: 'a_123',
|
||||
port: 'p_123',
|
||||
|
||||
@@ -9,17 +9,18 @@ import { setupApplicationTest } from 'ember-qunit';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { Response } from 'miragejs';
|
||||
import a11yAudit from 'ember-a11y-testing/test-support/audit';
|
||||
import sinon from 'sinon';
|
||||
import {
|
||||
currentSession,
|
||||
authenticateSession,
|
||||
invalidateSession,
|
||||
} from 'ember-simple-auth/test-support';
|
||||
import WindowMockIPC from '../helpers/window-mock-ipc';
|
||||
import setupStubs from 'api/test-support/handlers/client-daemon-search';
|
||||
|
||||
module('Acceptance | scopes', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupStubs(hooks);
|
||||
|
||||
const APP_STATE_TITLE = '.hds-application-state__title';
|
||||
|
||||
@@ -36,12 +37,6 @@ module('Acceptance | scopes', function (hooks) {
|
||||
session: null,
|
||||
};
|
||||
|
||||
const stubs = {
|
||||
global: null,
|
||||
org: null,
|
||||
ipcService: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
index: '/',
|
||||
clusterUrl: '/cluster-url',
|
||||
@@ -78,21 +73,20 @@ module('Acceptance | scopes', function (hooks) {
|
||||
|
||||
// create scopes
|
||||
instances.scopes.global = this.server.create('scope', { id: 'global' });
|
||||
stubs.global = { id: 'global', type: 'global' };
|
||||
const globalScope = { id: 'global', type: 'global' };
|
||||
instances.scopes.org = this.server.create('scope', {
|
||||
type: 'org',
|
||||
scope: stubs.global,
|
||||
scope: globalScope,
|
||||
});
|
||||
instances.scopes.org2 = this.server.create('scope', {
|
||||
type: 'org',
|
||||
scope: stubs.global,
|
||||
scope: globalScope,
|
||||
});
|
||||
stubs.org = { id: instances.scopes.org.id, type: 'org' };
|
||||
const orgScope = { id: instances.scopes.org.id, type: 'org' };
|
||||
instances.scopes.project = this.server.create('scope', {
|
||||
type: 'project',
|
||||
scope: stubs.org,
|
||||
scope: orgScope,
|
||||
});
|
||||
stubs.project = { id: instances.scopes.project.id, type: 'project' };
|
||||
|
||||
instances.authMethods.global = this.server.create('auth-method', {
|
||||
scope: instances.scopes.global,
|
||||
@@ -134,12 +128,8 @@ module('Acceptance | scopes', function (hooks) {
|
||||
this.owner.register('service:browser/window', WindowMockIPC);
|
||||
setDefaultClusterUrl(this);
|
||||
|
||||
const ipcService = this.owner.lookup('service:ipc');
|
||||
stubs.ipcService = sinon.stub(ipcService, 'invoke');
|
||||
});
|
||||
|
||||
hooks.afterEach(function () {
|
||||
sinon.restore();
|
||||
this.ipcStub.withArgs('isClientDaemonRunning').returns(true);
|
||||
this.stubClientDaemonSearch('targets');
|
||||
});
|
||||
|
||||
test('visiting index', async function (assert) {
|
||||
@@ -190,6 +180,7 @@ module('Acceptance | scopes', function (hooks) {
|
||||
|
||||
test('can navigate among org scopes via header navigation', async function (assert) {
|
||||
assert.expect(3);
|
||||
this.stubClientDaemonSearch('targets', 'targets', 'targets', 'targets');
|
||||
await visit(urls.targets);
|
||||
|
||||
await click('.rose-header-nav .rose-dropdown a:nth-of-type(2)');
|
||||
@@ -224,7 +215,8 @@ module('Acceptance | scopes', function (hooks) {
|
||||
|
||||
test('visiting empty targets', async function (assert) {
|
||||
assert.expect(1);
|
||||
this.server.get('/targets', () => new Response(200));
|
||||
this.server.db.targets.remove();
|
||||
this.stubClientDaemonSearch('targets');
|
||||
|
||||
await visit(urls.targets);
|
||||
|
||||
@@ -233,8 +225,8 @@ module('Acceptance | scopes', function (hooks) {
|
||||
|
||||
test.skip('connecting to a target', async function (assert) {
|
||||
assert.expect(3);
|
||||
stubs.ipcService.withArgs('cliExists').returns(true);
|
||||
stubs.ipcService.withArgs('connect').returns({
|
||||
this.ipcStub.ipcService.withArgs('cliExists').returns(true);
|
||||
this.ipcStub.ipcService.withArgs('connect').returns({
|
||||
session_id: instances.session.id,
|
||||
address: 'a_123',
|
||||
port: 'p_123',
|
||||
|
||||
Reference in new issue
Block a user