Merge pull request #503 from hashicorp/tests-coverage-desktop

Build all desktop tests in Github action
This commit is contained in:
Susmitha Girumala authored and GitHub committed 2021-03-24 10:15:03 -04:00
commit 22d578620d
13 files changed
+1912 -91

No files matched your search

+6 -3
View File
@@ -413,9 +413,12 @@ export default function () {
);
this.get('/sessions/:id', function ({ sessions }, { params: { id } }) {
const session = sessions.find(id);
return session.update({
status: pickRandomStatusString(),
});
if (!Ember.testing) {
session.update({
status: pickRandomStatusString(),
});
}
return session;
});
this.post(
'/sessions/:idMethod',
+1
View File
@@ -50,6 +50,7 @@
"clipboard": "^2.0.6",
"core": "*",
"doctoc": "^1.4.0",
"ember-a11y-testing": "^4.0.2",
"ember-auto-import": "^1.10.1",
"ember-autofocus-modifier": "^1.1.0",
"ember-cli": "~3.24.0",
@@ -2,7 +2,7 @@ import { module, test } from 'qunit';
import {
visit,
currentURL,
//fillIn,
fillIn,
click,
find,
//findAll,
@@ -12,8 +12,8 @@ import {
import { run, later } from '@ember/runloop';
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 { Response } from 'miragejs';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import {
currentSession,
authenticateSession,
@@ -37,8 +37,6 @@ module('Acceptance | authentication', function (hooks) {
global: null,
org: null,
},
hostCatalog: null,
target: null,
};
const stubs = {
@@ -60,6 +58,7 @@ module('Acceptance | authentication', function (hooks) {
},
},
targets: null,
sessions: null,
};
const setDefaultOrigin = (test) => {
@@ -93,21 +92,13 @@ module('Acceptance | authentication', function (hooks) {
scope: instances.scopes.org,
});
instances.hostCatalog = this.server.create(
'host-catalog',
{ scope: instances.scopes.project },
'withChildren'
);
instances.target = this.server.create(
'target',
{ scope: instances.scopes.project },
'withRandomHostSets'
);
urls.scopes.global = `/scopes/${instances.scopes.global.id}`;
urls.scopes.org = `/scopes/${instances.scopes.org.id}`;
urls.authenticate.global = `${urls.scopes.global}/authenticate`;
urls.authenticate.methods.global = `${urls.authenticate.global}/${instances.authMethods.global.id}`;
urls.projects = `${urls.scopes.global}/projects`;
urls.targets = `${urls.projects}/targets`;
urls.sessions = `${urls.projects}/sessions`;
class MockIPC {
origin = null;
@@ -147,7 +138,7 @@ module('Acceptance | authentication', function (hooks) {
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
assert.expect(2);
await visit(urls.index);
//await a11yAudit();
await a11yAudit();
assert.notOk(currentSession().isAuthenticated);
assert.equal(currentURL(), urls.authenticate.methods.global);
});
@@ -156,11 +147,89 @@ module('Acceptance | authentication', function (hooks) {
assert.expect(2);
instances.authMethods.global.destroy();
await visit(urls.authenticate.global);
//await a11yAudit();
await a11yAudit();
assert.equal(currentURL(), urls.authenticate.global);
assert.ok(find('.rose-message'));
});
test('visiting authenticate route without origin redirects to origin index', async function (assert) {
assert.expect(1);
this.owner.lookup('service:origin').rendererOrigin = null;
await visit(urls.authenticate.global);
await a11yAudit();
assert.equal(currentURL(), urls.origin);
});
test('visiting authenticate route when the scope cannot be loaded is allowed', async function (assert) {
assert.expect(1);
this.server.get('/scopes', () => {
return new Response(404);
});
await visit(urls.authenticate.global);
await a11yAudit();
assert.equal(currentURL(), urls.authenticate.methods.global);
});
test('failed authentication shows a notification message', async function (assert) {
assert.expect(3);
await visit(urls.authenticate.methods.global);
assert.notOk(currentSession().isAuthenticated);
await fillIn('[name="identification"]', 'error');
await click('[type="submit"]');
assert.ok(find('.rose-notification.is-error'));
assert.notOk(currentSession().isAuthenticated);
});
test('can reset origin before authentication', async function (assert) {
assert.expect(1);
await visit(urls.authenticate.methods.global);
await click('.change-origin a');
assert.equal(currentURL(), urls.origin);
});
test('deauthentication redirects to first global authenticate method', async function (assert) {
assert.expect(3);
await visit(urls.authenticate.methods.global);
await fillIn('[name="identification"]', 'test');
await fillIn('[name="password"]', 'test');
later(async () => {
run.cancelTimers();
assert.ok(currentSession().isAuthenticated);
await click('.rose-header-utilities .rose-dropdown summary');
assert.equal(
find(
'.rose-header-utilities .rose-dropdown-content button'
).textContent.trim(),
'Deauthenticate'
);
await click('.rose-header-utilities .rose-dropdown-content button');
assert.notOk(currentSession().isAuthenticated);
}, 750);
await click('[type="submit"]');
});
test('401 responses result in deauthentication', async function (assert) {
assert.expect(3);
await visit(urls.authenticate.methods.global);
await fillIn('[name="identification"]', 'test');
await fillIn('[name="password"]', 'test');
later(async () => {
run.cancelTimers();
assert.ok(
currentSession().isAuthenticated,
'Session begins authenticated, before encountering 401'
);
assert.ok(currentURL(), urls.targets);
this.server.get('/sessions', () => new Response(401));
await visit(urls.sessions);
assert.notOk(
currentSession().isAuthenticated,
'Session is unauthenticated, after encountering 401'
);
}, 750);
await click('[type="submit"]');
});
test('color theme is applied from session data', async function (assert) {
assert.expect(12);
authenticateSession({
+101 -16
View File
@@ -2,24 +2,27 @@ import { module, test } from 'qunit';
import {
visit,
currentURL,
//fillIn,
//click,
//find,
fillIn,
click,
find,
//findAll,
//getRootElement
//setupOnerror,
} from '@ember/test-helpers';
import { run, later } from '@ember/runloop';
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 a11yAudit from 'ember-a11y-testing/test-support/audit';
import sinon from 'sinon';
import {
//currentSession,
//authenticateSession,
currentSession,
// authenticateSession,
invalidateSession,
} from 'ember-simple-auth/test-support';
import config from '../../config/environment';
module('Acceptance | authentication', function (hooks) {
module('Acceptance | origin', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
@@ -43,6 +46,7 @@ module('Acceptance | authentication', function (hooks) {
const stubs = {
global: null,
org: null,
ipcService: null,
};
const urls = {
@@ -54,8 +58,11 @@ module('Acceptance | authentication', function (hooks) {
},
authenticate: {
global: null,
org: null,
methods: {
global: null,
},
},
projects: null,
targets: null,
};
@@ -76,13 +83,9 @@ module('Acceptance | authentication', function (hooks) {
});
stubs.project = { id: instances.scopes.project.id, type: 'project' };
// create other resources
instances.authMethods.global = this.server.create('auth-method', {
scope: instances.scopes.global,
});
instances.authMethods.org = this.server.create('auth-method', {
scope: instances.scopes.org,
});
instances.hostCatalog = this.server.create(
'host-catalog',
@@ -96,9 +99,10 @@ module('Acceptance | authentication', function (hooks) {
);
urls.scopes.global = `/scopes/${instances.scopes.global.id}`;
urls.scopes.org = `/scopes/${instances.scopes.org.id}`;
urls.authenticate.global = `${urls.scopes.global}/authenticate/${instances.authMethods.global.id}`;
urls.authenticate.org = `${urls.scopes.global}/authenticate/${instances.authMethods.org.id}`;
urls.authenticate.global = `${urls.scopes.global}/authenticate`;
urls.authenticate.methods.global = `${urls.authenticate.global}/${instances.authMethods.global.id}`;
urls.projects = `${urls.scopes.global}/projects`;
urls.targets = `${urls.projects}/targets`;
class MockIPC {
origin = null;
@@ -115,6 +119,8 @@ module('Acceptance | authentication', function (hooks) {
this.origin = origin;
return this.origin;
}
resetOrigin() {}
}
mockIPC = new MockIPC();
@@ -132,13 +138,92 @@ module('Acceptance | authentication', function (hooks) {
hooks.afterEach(function () {
window.removeEventListener('message', messageHandler);
sinon.restore();
});
test('visiting index', async function (assert) {
assert.expect(1);
await visit(urls.origin);
await a11yAudit();
assert.equal(currentURL(), urls.origin);
});
test('visiting index without an origin specified redirects to origin route', async function (assert) {
assert.expect(2);
await visit(urls.index);
//await a11yAudit();
await a11yAudit();
assert.notOk(mockIPC.origin);
assert.equal(currentURL(), urls.origin);
});
test('can set origin', async function (assert) {
assert.expect(2);
assert.notOk(mockIPC.origin);
await visit(urls.origin);
await a11yAudit();
await fillIn('[name="host"]', window.location.origin);
await click('[type="submit"]');
assert.equal(mockIPC.origin, window.location.origin);
});
test('captures error on origin update', async function (assert) {
assert.expect(2);
assert.notOk(mockIPC.origin);
sinon.stub(this.owner.lookup('service:origin'), 'setOrigin').throws();
await visit(urls.origin);
await a11yAudit();
await fillIn('[name="host"]', window.location.origin);
await click('[type="submit"]');
assert.ok(find('.rose-notification.is-error'));
});
test('can reset origin before authentication', async function (assert) {
assert.expect(4);
assert.notOk(mockIPC.origin);
await visit(urls.origin);
await fillIn('[name="host"]', window.location.origin);
await click('[type="submit"]');
assert.equal(mockIPC.origin, window.location.origin);
assert.equal(currentURL(), urls.authenticate.methods.global);
await click('.change-origin a');
assert.equal(currentURL(), urls.origin);
});
test('origin set automatically in dev mode', async function (assert) {
assert.expect(1);
config.autoOrigin = true;
await visit(urls.origin);
await fillIn('[name="host"]', window.location.origin);
await click('[type="submit"]');
assert.equal(
this.owner.lookup('controller:origin').origin,
window.location.origin
);
config.autoOrigin = false;
});
test('can reset origin on error', async function (assert) {
assert.expect(4);
await visit(urls.origin);
await a11yAudit();
await fillIn('[name="host"]', window.location.origin);
await click('[type="submit"]');
await fillIn('[name="identification"]', 'test');
await fillIn('[name="password"]', 'test');
this.server.get('/targets', () => new Response(500));
await later(async () => {
run.cancelTimers();
assert.ok(currentSession().isAuthenticated);
assert.equal(
find('main section button').textContent.trim(),
'Disconnect'
);
}, 750);
await click('[type="submit"]');
later(async () => {
assert.notOk(currentSession().isAuthenticated);
assert.equal(currentURL(), urls.authenticate.methods.global);
}, 750);
await click('main section button');
});
});
+3 -3
View File
@@ -12,7 +12,7 @@ import {
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 a11yAudit from 'ember-a11y-testing/test-support/audit';
import {
currentSession,
authenticateSession,
@@ -142,7 +142,7 @@ module('Acceptance | projects', function (hooks) {
invalidateSession();
assert.expect(2);
await visit(urls.projects);
//await a11yAudit();
await a11yAudit();
assert.notOk(currentSession().isAuthenticated);
assert.equal(currentURL(), urls.authenticate.methods.global);
});
@@ -150,7 +150,7 @@ module('Acceptance | projects', function (hooks) {
test('visiting index', async function (assert) {
assert.expect(1);
await visit(urls.projects);
//await a11yAudit();
await a11yAudit();
assert.equal(currentURL(), urls.projects);
});
});
@@ -0,0 +1,261 @@
import { module, test } from 'qunit';
import {
visit,
currentURL,
//fillIn,
click,
find,
findAll,
//getRootElement
//setupOnerror,
} from '@ember/test-helpers';
import { run, later } from '@ember/runloop';
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 {
currentSession,
authenticateSession,
invalidateSession,
} from 'ember-simple-auth/test-support';
module('Acceptance | projects | sessions', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
let mockIPC;
let messageHandler;
const instances = {
scopes: {
global: null,
org: null,
project: null,
},
authMethods: {
global: null,
},
user: null,
session: null,
};
const stubs = {
global: null,
org: null,
};
const urls = {
index: '/',
origin: '/origin',
scopes: {
global: null,
org: null,
},
authenticate: {
global: null,
methods: {
global: null,
},
},
projects: null,
sessions: null,
};
const setDefaultOrigin = (test) => {
const windowOrigin = window.location.origin;
const origin = test.owner.lookup('service:origin');
origin.rendererOrigin = windowOrigin;
};
hooks.beforeEach(function () {
instances.user = this.server.create('user', {
scope: instances.scopes.global,
});
authenticateSession({ user_id: instances.user.id });
// create scopes
instances.scopes.global = this.server.create('scope', { id: 'global' });
stubs.global = { id: 'global', type: 'global' };
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: stubs.global,
});
stubs.org = { id: instances.scopes.org.id, type: 'org' };
instances.scopes.project = this.server.create('scope', {
type: 'project',
scope: stubs.org,
});
stubs.project = { id: instances.scopes.project.id, type: 'project' };
instances.authMethods.global = this.server.create('auth-method', {
scope: instances.scopes.global,
});
instances.target = this.server.create(
'target',
{ scope: instances.scopes.project },
'withRandomHostSets'
);
instances.session = this.server.create(
'session',
{
scope: instances.scopes.project,
target: instances.target,
status: 'active',
user: instances.user,
},
'withAssociations'
);
urls.scopes.global = `/scopes/${instances.scopes.global.id}`;
urls.scopes.org = `/scopes/${instances.scopes.org.id}`;
urls.authenticate.global = `${urls.scopes.global}/authenticate`;
urls.authenticate.methods.global = `${urls.authenticate.global}/${instances.authMethods.global.id}`;
urls.projects = `${urls.scopes.org}/projects`;
urls.sessions = `${urls.projects}/sessions`;
class MockIPC {
origin = null;
invoke(method, payload) {
return this[method](payload);
}
getOrigin() {
return this.origin;
}
setOrigin(origin) {
this.origin = origin;
return this.origin;
}
}
mockIPC = new MockIPC();
messageHandler = async function (event) {
if (event.origin !== window.location.origin) return;
const { method, payload } = event.data;
if (method) {
const response = await mockIPC.invoke(method, payload);
event.ports[0].postMessage(response);
}
};
window.addEventListener('message', messageHandler);
setDefaultOrigin(this);
});
hooks.afterEach(function () {
window.removeEventListener('message', messageHandler);
});
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
invalidateSession();
assert.expect(2);
await visit(urls.sessions);
await a11yAudit();
assert.notOk(currentSession().isAuthenticated);
assert.equal(currentURL(), urls.authenticate.methods.global);
});
test('visiting index', async function (assert) {
assert.expect(2);
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 () => {
run.cancelTimers();
// await a11yAudit();
assert.equal(currentURL(), urls.sessions);
assert.equal(findAll('tbody tr').length, sessionsCount);
}, 750);
await visit(urls.sessions);
});
test('visiting empty sessions', async function (assert) {
assert.expect(1);
this.server.get('/sessions', () => new Response(200));
later(async () => {
run.cancelTimers();
await a11yAudit();
assert.ok(
find('.rose-message-title').textContent.trim(),
'No Sessions Available'
);
}, 750);
await visit(urls.sessions);
});
test('visiting sessions without targets is OK', async function (assert) {
assert.expect(2);
instances.session.update({ targetId: undefined });
const sessionsCount = this.server.schema.sessions.all().models.length;
later(async () => {
run.cancelTimers();
await a11yAudit();
assert.equal(currentURL(), urls.sessions);
assert.equal(findAll('tbody tr').length, sessionsCount);
}, 750);
await visit(urls.sessions);
});
test('can identify active sessions', async function (assert) {
assert.expect(1);
later(async () => {
run.cancelTimers();
assert.ok(
find('tbody tr:first-child th:first-child .session-status-active')
);
}, 750);
await visit(urls.sessions);
});
test('cannot identify pending sessions', async function (assert) {
assert.expect(1);
instances.session.update({ status: 'pending' });
later(async () => {
run.cancelTimers();
assert.notOk(
find('tbody tr:first-child th:first-child .session-status-active')
);
}, 750);
await visit(urls.sessions);
});
test('cannot identify terminated sessions', async function (assert) {
assert.expect(1);
instances.session.update({ status: 'terminated' });
later(async () => {
run.cancelTimers();
assert.notOk(
find('tbody tr:first-child th:first-child .session-status-active')
);
}, 750);
await visit(urls.sessions);
});
test('cancelling a session', async function (assert) {
assert.expect(1);
later(async () => {
run.cancelTimers();
await click('tbody tr:first-child td:last-child button');
assert.ok(find('[role="alert"].is-success'));
}, 750);
await visit(urls.sessions);
});
test('cancelling a session with error shows notification', async function (assert) {
assert.expect(1);
this.server.post('/sessions/:id_method', () => new Response(400));
later(async () => {
run.cancelTimers();
await click('tbody tr:first-child td:last-child button');
assert.ok(find('[role="alert"].is-error'));
}, 750);
await visit(urls.sessions);
});
});
@@ -0,0 +1,307 @@
import { module, test } from 'qunit';
import {
visit,
currentURL,
//fillIn,
click,
find,
findAll,
//getRootElement
//setupOnerror,
} from '@ember/test-helpers';
import { run, later } from '@ember/runloop';
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';
module('Acceptance | projects | targets', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
let mockIPC;
let messageHandler;
const instances = {
scopes: {
global: null,
org: null,
project: null,
},
authMethods: {
global: null,
},
target: null,
session: null,
};
const stubs = {
global: null,
org: null,
ipsService: null,
};
const urls = {
index: '/',
origin: '/origin',
scopes: {
global: null,
org: null,
},
authenticate: {
global: null,
methods: {
global: null,
},
},
projects: null,
targets: null,
target: null,
sessions: null,
};
const setDefaultOrigin = (test) => {
const windowOrigin = window.location.origin;
const origin = test.owner.lookup('service:origin');
origin.rendererOrigin = windowOrigin;
};
hooks.beforeEach(function () {
authenticateSession();
// create scopes
instances.scopes.global = this.server.create('scope', { id: 'global' });
stubs.global = { id: 'global', type: 'global' };
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: stubs.global,
});
stubs.org = { id: instances.scopes.org.id, type: 'org' };
instances.scopes.project = this.server.create('scope', {
type: 'project',
scope: stubs.org,
});
stubs.project = { id: instances.scopes.project.id, type: 'project' };
instances.authMethods.global = this.server.create('auth-method', {
scope: instances.scopes.global,
});
instances.hostCatalog = this.server.create(
'host-catalog',
{ scope: instances.scopes.project },
'withChildren'
);
instances.target = this.server.create(
'target',
{ scope: instances.scopes.project },
'withRandomHostSets'
);
instances.session = this.server.create(
'session',
{
scope: instances.scopes.project,
status: 'active',
},
'withAssociations'
);
urls.scopes.global = `/scopes/${instances.scopes.global.id}`;
urls.scopes.org = `/scopes/${instances.scopes.org.id}`;
urls.authenticate.global = `${urls.scopes.global}/authenticate`;
urls.authenticate.methods.global = `${urls.authenticate.global}/${instances.authMethods.global.id}`;
urls.projects = `${urls.scopes.org}/projects`;
urls.targets = `${urls.projects}/targets`;
urls.target = `${urls.targets}/${instances.target.id}`;
urls.sessions = `${urls.target}/sessions`;
class MockIPC {
origin = null;
invoke(method, payload) {
return this[method](payload);
}
getOrigin() {
return this.origin;
}
setOrigin(origin) {
this.origin = origin;
return this.origin;
}
}
mockIPC = new MockIPC();
messageHandler = async function (event) {
if (event.origin !== window.location.origin) return;
const { method, payload } = event.data;
if (method) {
const response = await mockIPC.invoke(method, payload);
event.ports[0].postMessage(response);
}
};
window.addEventListener('message', messageHandler);
setDefaultOrigin(this);
const ipcService = this.owner.lookup('service:ipc');
stubs.ipcService = sinon.stub(ipcService, 'invoke');
});
hooks.afterEach(function () {
window.removeEventListener('message', messageHandler);
});
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
invalidateSession();
assert.expect(2);
await visit(urls.targets);
await a11yAudit();
assert.notOk(currentSession().isAuthenticated);
assert.equal(currentURL(), urls.authenticate.methods.global);
});
test('visiting index', async function (assert) {
assert.expect(2);
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 () => {
run.cancelTimers();
await a11yAudit();
assert.equal(currentURL(), urls.targets);
assert.equal(findAll('tbody tr').length, targetsCount);
}, 750);
await visit(urls.targets);
});
test('visiting a target', async function (assert) {
assert.expect(1);
later(async () => {
run.cancelTimers();
await click('tbody tr th a');
assert.equal(currentURL(), urls.sessions);
}, 750);
await visit(urls.targets);
});
test('visiting empty targets', async function (assert) {
assert.expect(1);
this.server.get('/targets', () => new Response(200));
later(async () => {
run.cancelTimers();
assert.equal(
find('.rose-message-title').textContent.trim(),
'No Targets Available'
);
}, 750);
await visit(urls.targets);
});
test('connecting to a target', async function (assert) {
assert.expect(4);
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;
later(async () => {
run.cancelTimers();
await click(
'tbody tr:first-child td:last-child button',
'Activate connect mode'
);
assert.ok(find('.rose-dialog-success'), 'Success dialog');
assert.equal(findAll('.rose-dialog-footer button').length, 1);
assert.equal(
find('.rose-dialog-footer button').textContent.trim(),
'OK',
'Cannot retry'
);
assert.equal(
find('.rose-dialog-body .copyable-content').textContent.trim(),
'Local proxy address (tcp): a_123:p_123'
);
}, 750);
await visit(urls.targets);
});
test('handles cli error on connect', async function (assert) {
assert.expect(4);
stubs.ipcService.withArgs('cliExists').returns(true);
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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.equal(dialogButtons.length, 2);
assert.equal(dialogButtons[0].textContent.trim(), 'Retry', 'Can retry');
assert.equal(dialogButtons[1].textContent.trim(), 'Cancel', 'Can cancel');
}, 750);
await visit(urls.targets);
});
test('handles connect error', async function (assert) {
assert.expect(4);
stubs.ipcService.withArgs('cliExists').returns(true);
stubs.ipcService.withArgs('connect').rejects();
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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.equal(dialogButtons.length, 2);
assert.equal(dialogButtons[0].textContent.trim(), 'Retry', 'Can retry');
assert.equal(dialogButtons[1].textContent.trim(), 'Cancel', 'Can cancel');
}, 750);
await visit(urls.targets);
});
test('can retry on error', async function (assert) {
assert.expect(1);
stubs.ipcService.withArgs('cliExists').rejects();
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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.targets);
});
});
@@ -0,0 +1,298 @@
import { module, test } from 'qunit';
import {
visit,
currentURL,
//fillIn,
click,
find,
findAll,
//getRootElement
//setupOnerror,
} from '@ember/test-helpers';
import { run, later } from '@ember/runloop';
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';
module('Acceptance | projects | targets | hosts', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
let mockIPC;
let messageHandler;
const instances = {
scopes: {
global: null,
org: null,
project: null,
},
authMethods: {
global: null,
},
user: null,
target: null,
session: null,
};
const stubs = {
global: null,
org: null,
ipcService: null,
};
const urls = {
index: '/',
origin: '/origin',
scopes: {
global: null,
org: null,
},
authenticate: {
global: null,
methods: {
global: null,
},
},
projects: null,
targets: null,
target: null,
hosts: null,
};
const setDefaultOrigin = (test) => {
const windowOrigin = window.location.origin;
const origin = test.owner.lookup('service:origin');
origin.rendererOrigin = windowOrigin;
};
hooks.beforeEach(function () {
instances.user = this.server.create('user', {
scope: instances.scopes.global,
});
authenticateSession({ user_id: instances.user.id });
// create scopes
instances.scopes.global = this.server.create('scope', { id: 'global' });
stubs.global = { id: 'global', type: 'global' };
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: stubs.global,
});
stubs.org = { id: instances.scopes.org.id, type: 'org' };
instances.scopes.project = this.server.create('scope', {
type: 'project',
scope: stubs.org,
});
stubs.project = { id: instances.scopes.project.id, type: 'project' };
instances.authMethods.global = this.server.create('auth-method', {
scope: instances.scopes.global,
});
instances.hostCatalog = this.server.create(
'host-catalog',
{ scope: instances.scopes.project },
'withChildren'
);
instances.target = this.server.create('target', {
scope: instances.scopes.project,
hostSets: instances.hostCatalog.hostSets,
});
instances.session = this.server.create(
'session',
{
scope: instances.scopes.project,
status: 'active',
},
'withAssociations'
);
urls.scopes.global = `/scopes/${instances.scopes.global.id}`;
urls.scopes.org = `/scopes/${instances.scopes.org.id}`;
urls.authenticate.global = `${urls.scopes.global}/authenticate`;
urls.authenticate.methods.global = `${urls.authenticate.global}/${instances.authMethods.global.id}`;
urls.projects = `${urls.scopes.org}/projects`;
urls.targets = `${urls.projects}/targets`;
urls.target = `${urls.targets}/${instances.target.id}`;
urls.hosts = `${urls.target}/hosts`;
class MockIPC {
origin = null;
invoke(method, payload) {
return this[method](payload);
}
getOrigin() {
return this.origin;
}
setOrigin(origin) {
this.origin = origin;
return this.origin;
}
}
mockIPC = new MockIPC();
messageHandler = async function (event) {
if (event.origin !== window.location.origin) return;
const { method, payload } = event.data;
if (method) {
const response = await mockIPC.invoke(method, payload);
event.ports[0].postMessage(response);
}
};
window.addEventListener('message', messageHandler);
setDefaultOrigin(this);
const ipcService = this.owner.lookup('service:ipc');
stubs.ipcService = sinon.stub(ipcService, 'invoke');
});
hooks.afterEach(function () {
window.removeEventListener('message', messageHandler);
});
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
invalidateSession();
assert.expect(2);
await visit(urls.hosts);
await a11yAudit();
assert.notOk(currentSession().isAuthenticated);
assert.equal(currentURL(), urls.authenticate.methods.global);
});
test('visiting index', async function (assert) {
assert.expect(2);
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 () => {
run.cancelTimers();
// await a11yAudit();
assert.equal(currentURL(), urls.hosts);
assert.equal(findAll('tbody tr').length, hostsCount);
}, 750);
await visit(urls.hosts);
});
test('visiting empty hosts', async function (assert) {
assert.expect(1);
instances.target.update({ hostSets: [] });
later(async () => {
run.cancelTimers();
assert.ok(
find('.rose-message-title').textContent.trim(),
'No Hosts Available'
);
}, 750);
await visit(urls.hosts);
});
test('connecting to a host', async function (assert) {
assert.expect(3);
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;
later(async () => {
run.cancelTimers();
await click(
'tbody tr:first-child td:last-child button',
'Activate connect mode'
);
assert.ok(find('.rose-dialog-success'), 'Success dialog');
assert.equal(
find('.rose-dialog-footer .rose-button-primary').textContent.trim(),
'OK',
'Cannot retry'
);
assert.equal(
find('.rose-dialog-body .copyable-content').textContent.trim(),
'Local proxy address (tcp): a_123:p_123'
);
}, 750);
await visit(urls.hosts);
});
test('handles cli error on connect', async function (assert) {
assert.expect(4);
stubs.ipcService.withArgs('cliExists').returns(false);
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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.equal(dialogButtons.length, 2);
assert.equal(dialogButtons[0].textContent.trim(), 'Retry', 'Can retry');
assert.equal(dialogButtons[1].textContent.trim(), 'Cancel', 'Can cancel');
}, 750);
await visit(urls.hosts);
});
test('handles connect error', async function (assert) {
assert.expect(4);
stubs.ipcService.withArgs('cliExists').returns(true);
stubs.ipcService.withArgs('connect').rejects();
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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.equal(dialogButtons.length, 2);
assert.equal(dialogButtons[0].textContent.trim(), 'Retry', 'Can retry');
assert.equal(dialogButtons[1].textContent.trim(), 'Cancel', 'Can cancel');
}, 750);
await visit(urls.hosts);
});
test('can retry on error', async function (assert) {
assert.expect(1);
stubs.ipcService.withArgs('cliExists').rejects();
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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);
});
});
@@ -0,0 +1,381 @@
import { module, test } from 'qunit';
import {
visit,
currentURL,
//fillIn,
click,
find,
findAll,
//getRootElement
//setupOnerror,
} from '@ember/test-helpers';
import { run, later } from '@ember/runloop';
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';
module('Acceptance | projects | targets | sessions', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
let mockIPC;
let messageHandler;
const instances = {
scopes: {
global: null,
org: null,
project: null,
},
authMethods: {
global: null,
},
user: null,
target: null,
session: null,
};
const stubs = {
global: null,
org: null,
ipcService: null,
};
const urls = {
index: '/',
origin: '/origin',
scopes: {
global: null,
org: null,
},
authenticate: {
global: null,
methods: {
global: null,
},
},
projects: null,
targets: null,
target: null,
sessions: null,
};
const setDefaultOrigin = (test) => {
const windowOrigin = window.location.origin;
const origin = test.owner.lookup('service:origin');
origin.rendererOrigin = windowOrigin;
};
hooks.beforeEach(function () {
instances.user = this.server.create('user', {
scope: instances.scopes.global,
});
authenticateSession({ user_id: instances.user.id });
// create scopes
instances.scopes.global = this.server.create('scope', { id: 'global' });
stubs.global = { id: 'global', type: 'global' };
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: stubs.global,
});
stubs.org = { id: instances.scopes.org.id, type: 'org' };
instances.scopes.project = this.server.create('scope', {
type: 'project',
scope: stubs.org,
});
stubs.project = { id: instances.scopes.project.id, type: 'project' };
instances.authMethods.global = this.server.create('auth-method', {
scope: instances.scopes.global,
});
instances.hostCatalog = this.server.create(
'host-catalog',
{ scope: instances.scopes.project },
'withChildren'
);
instances.target = this.server.create(
'target',
{ scope: instances.scopes.project },
'withRandomHostSets'
);
instances.session = this.server.create(
'session',
{
scope: instances.scopes.project,
target: instances.target,
status: 'active',
user: instances.user,
},
'withAssociations'
);
urls.scopes.global = `/scopes/${instances.scopes.global.id}`;
urls.scopes.org = `/scopes/${instances.scopes.org.id}`;
urls.authenticate.global = `${urls.scopes.global}/authenticate`;
urls.authenticate.methods.global = `${urls.authenticate.global}/${instances.authMethods.global.id}`;
urls.projects = `${urls.scopes.org}/projects`;
urls.targets = `${urls.projects}/targets`;
urls.target = `${urls.targets}/${instances.target.id}`;
urls.sessions = `${urls.target}/sessions`;
class MockIPC {
origin = null;
invoke(method, payload) {
return this[method](payload);
}
getOrigin() {
return this.origin;
}
setOrigin(origin) {
this.origin = origin;
return this.origin;
}
}
mockIPC = new MockIPC();
messageHandler = async function (event) {
if (event.origin !== window.location.origin) return;
const { method, payload } = event.data;
if (method) {
const response = await mockIPC.invoke(method, payload);
event.ports[0].postMessage(response);
}
};
window.addEventListener('message', messageHandler);
setDefaultOrigin(this);
const ipcService = this.owner.lookup('service:ipc');
stubs.ipcService = sinon.stub(ipcService, 'invoke');
});
hooks.afterEach(function () {
window.removeEventListener('message', messageHandler);
});
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
invalidateSession();
assert.expect(2);
await visit(urls.sessions);
await a11yAudit();
assert.notOk(currentSession().isAuthenticated);
assert.equal(currentURL(), urls.authenticate.methods.global);
});
test('visiting index', async function (assert) {
assert.expect(2);
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 () => {
run.cancelTimers();
// await a11yAudit();
assert.equal(currentURL(), urls.sessions);
assert.equal(findAll('tbody tr').length, sessionsCount);
}, 750);
await visit(urls.sessions);
});
test('visiting index redirects to sessions', async function (assert) {
assert.expect(1);
later(async () => {
run.cancelTimers();
// await a11yAudit();
assert.equal(currentURL(), urls.sessions);
}, 750);
await visit(urls.target);
});
test('visiting empty sessions', async function (assert) {
assert.expect(1);
this.server.get('/sessions', () => new Response(200));
later(async () => {
run.cancelTimers();
assert.ok(
find('.rose-message-title').textContent.trim(),
'No Sessions Available'
);
}, 750);
await visit(urls.sessions);
});
test('can identify target with active sessions', async function (assert) {
assert.expect(1);
later(async () => {
run.cancelTimers();
assert.ok(find('.rose-layout-page-header .rose-badge-success'));
}, 750);
await visit(urls.sessions);
});
test('can identify target with pending sessions', async function (assert) {
assert.expect(1);
instances.session.update({ status: 'pending' });
later(async () => {
run.cancelTimers();
assert.ok(find('.rose-layout-page-header .rose-badge-success'));
}, 750);
await visit(urls.sessions);
});
test('cannot identify target with terminated sessions', async function (assert) {
assert.expect(1);
instances.session.update({ status: 'terminated' });
later(async () => {
run.cancelTimers();
assert.notOk(find('.rose-layout-page-header .rose-badge-success'));
}, 750);
await visit(urls.sessions);
});
test('cancelling a session', async function (assert) {
assert.expect(2);
const sessionsCount = this.server.schema.sessions.all().models.length;
later(async () => {
run.cancelTimers();
await click('tbody tr:first-child td:last-child button');
assert.ok(find('[role="alert"].is-success'));
assert.equal(findAll('tbody tr').length, sessionsCount - 1);
}, 750);
await visit(urls.sessions);
});
test('cancelling a session with error shows notification', async function (assert) {
assert.expect(1);
this.server.post('/sessions/:id_method', () => new Response(400));
later(async () => {
run.cancelTimers();
await click('tbody tr:first-child td:last-child button');
assert.ok(find('[role="alert"].is-error'));
}, 750);
await visit(urls.sessions);
});
test('connecting to a target', async function (assert) {
assert.expect(3);
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;
later(async () => {
run.cancelTimers();
await click('.rose-layout-page-actions button', 'Activate connect mode');
assert.ok(find('.rose-dialog-success'), 'Success dialog');
assert.equal(
find('.rose-dialog-footer .rose-button-primary').textContent.trim(),
'OK',
'Cannot retry'
);
assert.equal(
find('.rose-dialog-body .copyable-content').textContent.trim(),
'Local proxy address (tcp): a_123:p_123'
);
}, 750);
await visit(urls.sessions);
});
test('connect details in session', async function (assert) {
assert.expect(2);
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;
later(async () => {
run.cancelTimers();
instances.session.update('status', 'active');
await click('.rose-layout-page-actions button', 'Activate connect mode');
assert.ok(find('.rose-dialog-success'), 'Success dialog');
await click('.rose-dialog-dismiss');
assert.equal(
find(
'tbody tr:first-child td:nth-child(2) .copyable-content'
).textContent.trim(),
'a_123:p_123'
);
}, 750);
await visit(urls.sessions);
});
test('handles cli error on connect', async function (assert) {
assert.expect(4);
stubs.ipcService.withArgs('cliExists').returns(false);
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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.equal(dialogButtons.length, 2);
assert.equal(dialogButtons[0].textContent.trim(), 'Retry', 'Can retry');
assert.equal(dialogButtons[1].textContent.trim(), 'Cancel', 'Can cancel');
}, 750);
await visit(urls.sessions);
});
test('handles connect error', async function (assert) {
assert.expect(4);
stubs.ipcService.withArgs('cliExists').returns(true);
stubs.ipcService.withArgs('connect').rejects();
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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.equal(dialogButtons.length, 2);
assert.equal(dialogButtons[0].textContent.trim(), 'Retry', 'Can retry');
assert.equal(dialogButtons[1].textContent.trim(), 'Cancel', 'Can cancel');
}, 750);
await visit(urls.sessions);
});
test('can retry on error', async function (assert) {
assert.expect(1);
stubs.ipcService.withArgs('cliExists').rejects();
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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);
});
});
+348
View File
@@ -0,0 +1,348 @@
import { module, test } from 'qunit';
import {
visit,
currentURL,
//fillIn,
click,
find,
findAll,
//getRootElement
//setupOnerror,
} from '@ember/test-helpers';
import { run, later } from '@ember/runloop';
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';
module('Acceptance | scopes', function (hooks) {
setupApplicationTest(hooks);
setupMirage(hooks);
let mockIPC;
let messageHandler;
const instances = {
scopes: {
global: null,
org: null,
project: null,
},
authMethods: {
global: null,
},
target: null,
session: null,
};
const stubs = {
global: null,
org: null,
ipcService: null,
};
const urls = {
index: '/',
origin: '/origin',
scopes: {
global: null,
org: null,
org2: null,
},
authenticate: {
global: null,
methods: {
global: null,
},
},
projects: null,
org2Projects: null,
globalProjects: null,
targets: null,
org2Targets: null,
globalTargets: null,
target: null,
targetSessions: null,
};
const setDefaultOrigin = (test) => {
const windowOrigin = window.location.origin;
const origin = test.owner.lookup('service:origin');
origin.rendererOrigin = windowOrigin;
};
hooks.beforeEach(function () {
authenticateSession();
// create scopes
instances.scopes.global = this.server.create('scope', { id: 'global' });
stubs.global = { id: 'global', type: 'global' };
instances.scopes.org = this.server.create('scope', {
type: 'org',
scope: stubs.global,
});
instances.scopes.org2 = this.server.create('scope', {
type: 'org',
scope: stubs.global,
});
stubs.org = { id: instances.scopes.org.id, type: 'org' };
instances.scopes.project = this.server.create('scope', {
type: 'project',
scope: stubs.org,
});
stubs.project = { id: instances.scopes.project.id, type: 'project' };
instances.authMethods.global = this.server.create('auth-method', {
scope: instances.scopes.global,
});
instances.hostCatalog = this.server.create(
'host-catalog',
{ scope: instances.scopes.project },
'withChildren'
);
instances.target = this.server.create(
'target',
{ scope: instances.scopes.project },
'withRandomHostSets'
);
instances.session = this.server.create(
'session',
{
scope: instances.scopes.project,
status: 'active',
},
'withAssociations'
);
urls.scopes.global = `/scopes/${instances.scopes.global.id}`;
urls.scopes.org = `/scopes/${instances.scopes.org.id}`;
urls.scopes.org2 = `/scopes/${instances.scopes.org2.id}`;
urls.authenticate.global = `${urls.scopes.global}/authenticate`;
urls.authenticate.methods.global = `${urls.authenticate.global}/${instances.authMethods.global.id}`;
urls.projects = `${urls.scopes.org}/projects`;
urls.org2Projects = `${urls.scopes.org2}/projects`;
urls.globalProjects = `${urls.scopes.global}/projects`;
urls.targets = `${urls.projects}/targets`;
urls.org2Targets = `${urls.org2Projects}/targets`;
urls.globalTargets = `${urls.globalProjects}/targets`;
urls.target = `${urls.targets}/${instances.target.id}`;
urls.targetSessions = `${urls.target}/sessions`;
class MockIPC {
origin = null;
invoke(method, payload) {
return this[method](payload);
}
getOrigin() {
return this.origin;
}
setOrigin(origin) {
this.origin = origin;
return this.origin;
}
}
mockIPC = new MockIPC();
messageHandler = async function (event) {
if (event.origin !== window.location.origin) return;
const { method, payload } = event.data;
if (method) {
const response = await mockIPC.invoke(method, payload);
event.ports[0].postMessage(response);
}
};
window.addEventListener('message', messageHandler);
setDefaultOrigin(this);
const ipcService = this.owner.lookup('service:ipc');
stubs.ipcService = sinon.stub(ipcService, 'invoke');
});
hooks.afterEach(function () {
window.removeEventListener('message', messageHandler);
});
test('visiting index', async function (assert) {
assert.expect(2);
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 () => {
run.cancelTimers();
await a11yAudit();
assert.equal(currentURL(), urls.targets);
assert.equal(findAll('tbody tr').length, targetsCount);
}, 750);
await visit(urls.targets);
});
test('visiting global scope', async function (assert) {
assert.expect(1);
later(async () => {
run.cancelTimers();
await a11yAudit();
assert.equal(currentURL(), urls.globalTargets);
}, 750);
await visit(urls.scopes.global);
});
// TODO: this probably shouldn't be the case, but was setup to enable
// authentication when the global scope couldn't be loaded.
// In order to resolve this, we might hoist authentication routes up from
// under scopes.
test('visiting global scope is not successful when the global scope cannot be fetched', async function (assert) {
assert.expect(1);
this.server.get('/scopes/:id', ({ scopes }, { params: { id } }) => {
const scope = scopes.find(id);
const response = id === 'global' ? new Response(404) : scope;
return response;
});
later(async () => {
run.cancelTimers();
await a11yAudit();
assert.equal(currentURL(), urls.globalTargets);
}, 750);
await visit(urls.scopes.global);
});
test('visiting org scope', async function (assert) {
assert.expect(1);
later(async () => {
run.cancelTimers();
await a11yAudit();
assert.equal(currentURL(), urls.targets);
}, 750);
await visit(urls.scopes.org);
});
test('can navigate among org scopes via header navigation', async function (assert) {
assert.expect(3);
await later(async () => run.cancelTimers(), 750);
await visit(urls.targets);
await later(async () => assert.equal(currentURL(), urls.targets), 750);
await click('.rose-header-nav .rose-dropdown a:nth-of-type(2)');
await later(async () => assert.equal(currentURL(), urls.org2Targets), 750);
await click('.rose-header-nav .rose-dropdown a:nth-of-type(3)');
later(async () => assert.equal(currentURL(), urls.globalTargets), 750);
await click('.rose-header-nav .rose-dropdown a:nth-of-type(1)');
});
test('visiting index while unauthenticated redirects to global authenticate method', async function (assert) {
invalidateSession();
assert.expect(2);
await visit(urls.targets);
await a11yAudit();
assert.notOk(currentSession().isAuthenticated);
assert.equal(currentURL(), urls.authenticate.methods.global);
});
test('visiting a target', async function (assert) {
assert.expect(1);
later(async () => {
run.cancelTimers();
await click('tbody tr th a');
assert.equal(currentURL(), urls.targetSessions);
}, 750);
await visit(urls.targets);
});
test('visiting empty targets', async function (assert) {
assert.expect(1);
this.server.get('/targets', () => new Response(200));
later(async () => {
run.cancelTimers();
assert.ok(
find('.rose-message-title').textContent.trim(),
'No Targets Available'
);
}, 750);
await visit(urls.targets);
});
test('connecting to a target', async function (assert) {
assert.expect(3);
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;
later(async () => {
run.cancelTimers();
await click(
'tbody tr:first-child td:last-child button',
'Activate connect mode'
);
assert.ok(find('.rose-dialog-success'), 'Success dialog');
assert.equal(findAll('.rose-dialog-footer button').length, 1);
assert.equal(
find('.rose-dialog-footer button').textContent.trim(),
'OK',
'Cannot retry'
);
}, 750);
await visit(urls.targets);
});
test('handles cli error on connect', async function (assert) {
assert.expect(4);
stubs.ipcService.withArgs('cliExists').returns(false);
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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.equal(dialogButtons.length, 2);
assert.equal(dialogButtons[0].textContent.trim(), 'Retry', 'Can retry');
assert.equal(dialogButtons[1].textContent.trim(), 'Cancel', 'Can cancel');
}, 750);
await visit(urls.scopes.global);
});
test('handles connect error', async function (assert) {
assert.expect(4);
stubs.ipcService.withArgs('cliExists').returns(true);
stubs.ipcService.withArgs('connect').rejects();
const confirmService = this.owner.lookup('service:confirm');
confirmService.enabled = true;
later(async () => {
run.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.equal(dialogButtons.length, 2);
assert.equal(dialogButtons[0].textContent.trim(), 'Retry', 'Can retry');
assert.equal(dialogButtons[1].textContent.trim(), 'Cancel', 'Can cancel');
}, 750);
await visit(urls.targets);
});
});
@@ -1,26 +1,27 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render } from '@ember/test-helpers';
import { render, find } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
module('Integration | Component | branded-card', function (hooks) {
setupRenderingTest(hooks);
test('it renders', async function (assert) {
// Set any properties with this.set('myProperty', 'value');
// Handle any actions with this.set('myAction', function(val) { ... });
await render(hbs`<BrandedCard />`);
assert.ok(find('.branded-card'));
assert.ok(find('.branded-card-title'));
assert.ok(find('.branded-card-description'));
});
assert.equal(this.element.textContent.trim(), '');
// Template block usage:
await render(hbs`
<BrandedCard>
template block text
</BrandedCard>
`);
assert.equal(this.element.textContent.trim(), 'template block text');
test('it renders with content', async function (assert) {
await render(hbs`<BrandedCard
@title="title"
@description="description"
/>`);
assert.equal(find('.branded-card-title').textContent.trim(), 'title');
assert.equal(
find('.branded-card-description').textContent.trim(),
'description'
);
});
});
+19 -4
View File
@@ -1,12 +1,27 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import sinon from 'sinon';
module('Unit | Service | origin', function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let service = this.owner.lookup('service:origin');
assert.ok(service);
let service, ipcService;
hooks.beforeEach(function () {
service = this.owner.lookup('service:origin');
ipcService = this.owner.lookup('service:ipc');
});
test('resets origin on error', async function (assert) {
assert.expect(4);
const ipcServiceStubbed = sinon.stub(ipcService, 'invoke');
await service.setOrigin(window.location.origin);
assert.equal(service.rendererOrigin, window.location.origin);
assert.equal(service.adapter.host, window.location.origin);
ipcServiceStubbed.withArgs('setOrigin').rejects();
service.setOrigin('invalid-origin').catch(() => {
assert.notOk(service.rendererOrigin);
assert.equal(service.adapter.host, window.location.origin);
});
});
});
+85 -33
View File
@@ -4174,6 +4174,11 @@ axe-core@3.5.5:
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-3.5.5.tgz#84315073b53fa3c0c51676c588d59da09a192227"
integrity sha512-5P0QZ6J5xGikH780pghEdbEKijCTrruK9KxtPZCFWUpef0f6GipO+xEZ5GKCb020mmqgbiNO6TcA55CriL784Q==
axe-core@^4.1.2:
version "4.1.3"
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.1.3.tgz#64a4c85509e0991f5168340edc4bedd1ceea6966"
integrity sha512-vwPpH4Aj4122EW38mxO/fxhGKtwWTMLDIJfZ1He0Edbtjcfna/R3YB67yVhezUMzqc3Jr3+Ii50KRntlENL4xQ==
babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@@ -6495,9 +6500,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001125, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001181:
version "1.0.30001187"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001187.tgz#5706942631f83baa5a0218b7dfa6ced29f845438"
integrity sha512-w7/EP1JRZ9552CyrThUnay2RkZ1DXxKe/Q2swTC4+LElLh9RRYrL1Z+27LlakB8kzY0fSmHw9mc7XYDUKAKWMA==
version "1.0.30001204"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001204.tgz#256c85709a348ec4d175e847a3b515c66e79f2aa"
integrity sha512-JUdjWpcxfJ9IPamy2f5JaRDCaqJOxDzOSKtbdx4rH9VivMd1vIzoPumsJa9LoMIi4Fx2BV2KZOxWhNkBjaYivQ==
capture-exit@^2.0.0:
version "2.0.0"
@@ -7736,6 +7741,11 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
date-and-time@^0.14.1:
version "0.14.2"
resolved "https://registry.yarnpkg.com/date-and-time/-/date-and-time-0.14.2.tgz#a4266c3dead460f6c231fe9674e585908dac354e"
integrity sha512-EFTCh9zRSEpGPmJaexg7HTuzZHh6cnJj1ui7IGCFNXzd2QdpsNh05Db5TF3xzJm30YN+A8/6xHSuRcQqoc3kFA==
date-time@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/date-time/-/date-time-2.1.0.tgz#0286d1b4c769633b3ca13e1e62558d2dbdc2eba2"
@@ -7785,7 +7795,7 @@ debug@~4.1.0:
dependencies:
ms "^2.1.1"
debuglog@^1.0.1:
debuglog@*, debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"
integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI=
@@ -8350,9 +8360,9 @@ electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.649:
integrity sha512-ojGDupQ3UMkvPWcTICe4JYe17+o9OLiFMPoduoR72Zp2ILt1mRVeqnxBEd6s/ptekrnsFU+0A4lStfBe/wyG/A==
electron-to-chromium@^1.3.564:
version "1.3.687"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.687.tgz#c336184b7ab70427ffe2ee79eaeaedbc1ad8c374"
integrity sha512-IpzksdQNl3wdgkzf7dnA7/v10w0Utf1dF2L+B4+gKrloBrxCut+au+kky3PYvle3RMdSxZP+UiCZtLbcYRxSNQ==
version "1.3.693"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.693.tgz#5089c506a925c31f93fcb173a003a22e341115dd"
integrity sha512-vUdsE8yyeu30RecppQtI+XTz2++LWLVEIYmzeCaCRLSdtKZ2eXqdJcrs85KwLiPOPVc6PELgWyXBsfqIvzGZag==
element-resize-detector@^1.2.1:
version "1.2.1"
@@ -8385,6 +8395,24 @@ ember-a11y-testing@^3.0.0:
ember-cli-version-checker "5.1.1"
ember-get-config "0.2.4"
ember-a11y-testing@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/ember-a11y-testing/-/ember-a11y-testing-4.0.2.tgz#960ee9582bd315bd16d9dedbf450fd508d021d99"
integrity sha512-3pa+6bibbY4Vsl3uKFpD0OH4Mzd8NQUwenaWWWngiJo+R/U6AgJllxo/apORzRgvLxKtBCEpXVocUp/gkZ3rOQ==
dependencies:
axe-core "^4.1.2"
body-parser "^1.19.0"
broccoli-funnel "^3.0.3"
broccoli-merge-trees "^4.2.0"
date-and-time "^0.14.1"
ember-auto-import "^1.10.1"
ember-cli-babel "^7.21.0"
ember-cli-typescript "^3.0.0"
ember-cli-version-checker "^5.1.2"
ember-destroyable-polyfill "^2.0.1"
fs-extra "^9.0.1"
validate-peer-dependencies "^1.1.0"
ember-assign-polyfill@^2.6.0:
version "2.7.2"
resolved "https://registry.yarnpkg.com/ember-assign-polyfill/-/ember-assign-polyfill-2.7.2.tgz#58f6f60235126cb23df248c846008fa9a3245fc1"
@@ -8833,7 +8861,7 @@ ember-cli-typescript@^2.0.0-rc.2, ember-cli-typescript@^2.0.2:
stagehand "^1.0.0"
walk-sync "^1.0.0"
ember-cli-typescript@^3.1.3, ember-cli-typescript@^3.1.4:
ember-cli-typescript@^3.0.0, ember-cli-typescript@^3.1.3, ember-cli-typescript@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/ember-cli-typescript/-/ember-cli-typescript-3.1.4.tgz#21d6ccd670d1f2e34c9cce68c6e32c442f46806b"
integrity sha512-HJ73kL45OGRmIkPhBNFt31I1SGUvdZND+LCH21+qpq3pPlFpJG8GORyXpP+2ze8PbnITNLzwe5AwUrpyuRswdQ==
@@ -9120,7 +9148,7 @@ ember-decorators-polyfill@^1.1.5:
ember-cli-version-checker "^3.1.3"
ember-compatibility-helpers "^1.2.0"
ember-destroyable-polyfill@^2.0.2, ember-destroyable-polyfill@^2.0.3:
ember-destroyable-polyfill@^2.0.1, ember-destroyable-polyfill@^2.0.2, ember-destroyable-polyfill@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/ember-destroyable-polyfill/-/ember-destroyable-polyfill-2.0.3.tgz#1673ed66609a82268ef270a7d917ebd3647f11e1"
integrity sha512-TovtNqCumzyAiW0/OisSkkVK93xnVF4NRU6+FN0ubpfwEOpRrmM2RqDwXI6YAChCgSHON1cz0DfQStpA1Gjuuw==
@@ -12264,9 +12292,9 @@ heimdalljs@^0.3.0:
rsvp "~3.2.1"
highlight.js@^10.1.1, highlight.js@^10.4.1, highlight.js@~10.5.0:
version "10.6.0"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.6.0.tgz#0073aa71d566906965ba6e1b7be7b2682f5e18b6"
integrity sha512-8mlRcn5vk/r4+QcqerapwBYTe+iPL5ih6xrNylxrnBdHQiijDETfXX7VIxC3UiCRiINBJfANBAsPzAvRQj8RpQ==
version "10.7.1"
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.1.tgz#a8ec4152db24ea630c90927d6cae2a45f8ecb955"
integrity sha512-S6G97tHGqJ/U8DsXcEdnACbirtbx58Bx9CzIVeYli8OuswCfYI/LsXH2EiGcoGio1KAC3x4mmUwulOllJ2ZyRA==
hmac-drbg@^1.0.1:
version "1.0.1"
@@ -12316,6 +12344,13 @@ hosted-git-info@^3.0.2, hosted-git-info@^3.0.6:
dependencies:
lru-cache "^6.0.0"
hosted-git-info@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-4.0.1.tgz#710ef5452ea429a844abc33c981056e7371edab7"
integrity sha512-eT7NrxAsppPRQEBSwKSosReE+v8OzABwEScQYk5d4uxaEPlzxTIku7LINXtBGalthkLhJnq5lBI89PfK43zAKg==
dependencies:
lru-cache "^6.0.0"
html-encoding-sniffer@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3"
@@ -12600,9 +12635,9 @@ ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8:
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
immer@8.0.1, immer@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.1.tgz#9c73db683e2b3975c424fb0572af5889877ae656"
integrity sha512-aqXhGP7//Gui2+UrEtvxZxSquQVXTpZ7KDxfCcKAF3Vysvw0CViVaW9RZ1j1xlIYqaaaipBoqdqeibkc18PNvA==
version "8.0.4"
resolved "https://registry.yarnpkg.com/immer/-/immer-8.0.4.tgz#3a21605a4e2dded852fb2afd208ad50969737b7a"
integrity sha512-jMfL18P+/6P6epANRvRk6q8t+3gGhqsJ9EuJ25AXE+9bNTYtssvzeYbEd0mXRYWCmmXSIbnlpz6vd6iJlmGGGQ==
import-cwd@^2.0.0:
version "2.1.0"
@@ -12644,7 +12679,7 @@ import-lazy@^4.0.0:
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153"
integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==
imurmurhash@^0.1.4:
imurmurhash@*, imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
@@ -14327,6 +14362,11 @@ lodash._baseflatten@^3.0.0:
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
lodash._baseindexof@*:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"
integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw=
lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
@@ -14335,11 +14375,16 @@ lodash._baseuniq@~4.6.0:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"
lodash._bindcallback@^3.0.0:
lodash._bindcallback@*, lodash._bindcallback@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4=
lodash._cacheindexof@*:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"
integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI=
lodash._createassigner@^3.0.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11"
@@ -14349,12 +14394,19 @@ lodash._createassigner@^3.0.0:
lodash._isiterateecall "^3.0.0"
lodash.restparam "^3.0.0"
lodash._createcache@*:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM=
dependencies:
lodash._getnative "^3.0.0"
lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"
integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=
lodash._getnative@^3.0.0:
lodash._getnative@*, lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=
@@ -14567,7 +14619,7 @@ lodash.pick@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3"
integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=
lodash.restparam@^3.0.0:
lodash.restparam@*, lodash.restparam@^3.0.0:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=
@@ -15785,9 +15837,9 @@ node-modules-regexp@^1.0.0:
integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
node-notifier@^5.0.1, node-notifier@^8.0.1:
version "8.0.1"
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.1.tgz#f86e89bbc925f2b068784b31f382afdc6ca56be1"
integrity sha512-BvEXF+UmsnAfYfoapKM9nGxnP+Wn7P91YfXmrKnfcYCx6VBeoN5Ez5Ogck6I8Bi5k4RlpqRYaw75pAwzX9OphA==
version "8.0.2"
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-8.0.2.tgz#f3167a38ef0d2c8a866a83e318c1ba0efeb702c5"
integrity sha512-oJP/9NAdd9+x2Q+rfphB2RJCHjod70RcRLjosiPMMu5gjIfwVnOUGq2nbTjTUbmy0DJ/tFIVT30+Qe3nzl4TJg==
dependencies:
growly "^1.3.0"
is-wsl "^2.2.0"
@@ -15995,12 +16047,12 @@ npm-package-arg@^7.0.0:
validate-npm-package-name "^3.0.0"
npm-package-arg@^8.0.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.0.tgz#b5f6319418c3246a1c38e1a8fbaa06231bc5308f"
integrity sha512-/ep6QDxBkm9HvOhOg0heitSd7JHA1U7y1qhhlRlteYYAi9Pdb/ZV7FW5aHpkrpM8+P+4p/jjR8zCyKPBMBjSig==
version "8.1.2"
resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.2.tgz#b868016ae7de5619e729993fbd8d11dc3c52ab62"
integrity sha512-6Eem455JsSMJY6Kpd3EyWE+n5hC+g9bSyHr9K9U2zqZb7+02+hObQ2c0+8iDk/mNF+8r1MhY44WypKJAkySIYA==
dependencies:
hosted-git-info "^3.0.6"
semver "^7.0.0"
hosted-git-info "^4.0.1"
semver "^7.3.4"
validate-npm-package-name "^3.0.0"
npm-package-arg@^8.1.0:
@@ -17671,9 +17723,9 @@ query-string@^5.0.1:
strict-uri-encode "^1.0.0"
query-string@^6.8.2:
version "6.14.0"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.0.tgz#0b7b7ca326f5facf10dd2d45d26645cd287f8c92"
integrity sha512-In3o+lUxlgejoVJgwEdYtdxrmlL0cQWJXj0+kkI7RWVo7hg5AhFtybeKlC9Dpgbr8eOC4ydpEh8017WwyfzqVQ==
version "6.14.1"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a"
integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw==
dependencies:
decode-uri-component "^0.2.0"
filter-obj "^1.1.0"
@@ -19519,9 +19571,9 @@ socks-proxy-agent@^5.0.0:
socks "^2.3.3"
socks@^2.3.3:
version "2.5.1"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.5.1.tgz#7720640b6b5ec9a07d556419203baa3f0596df5f"
integrity sha512-oZCsJJxapULAYJaEYBSzMcz8m3jqgGrHaGhkmU/o/PQfFWYWxkAaA0UMGImb6s6tEXfKi959X6VJjMMQ3P6TTQ==
version "2.6.0"
resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.0.tgz#6b984928461d39871b3666754b9000ecf39dfac2"
integrity sha512-mNmr9owlinMplev0Wd7UHFlqI4ofnBnNzFuzrm63PPaHgbkqCFe4T5LzwKmtQ/f2tX0NTpcdVLyD/FHxFBstYw==
dependencies:
ip "^1.1.5"
smart-buffer "^4.1.0"