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:
Zhihe Li committed 2024-01-16 10:17:45 -05:00
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;
+8 -4
View File
@@ -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|>
+1 -1
View File
@@ -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',
+14 -22
View File
@@ -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',