Merge pull request #889 from hashicorp/refactor-polling

Replace ember-concurrency with ember-pollster
This commit is contained in:
Susmitha Girumala authored and GitHub committed 2022-01-04 09:38:13 -05:00
commit 7f2b753295
14 files changed
+396 -524

No files matched your search

@@ -1,42 +1,22 @@
import Route from '@ember/routing/route';
import { getOwner } from '@ember/application';
import { inject as service } from '@ember/service';
import { task, timeout } from 'ember-concurrency';
import { action } from '@ember/object';
import runEvery from 'ember-pollster/decorators/route/run-every';
import { notifyError } from 'core/decorators/notify';
import config from '../../../../../config/environment';
const POLL_TIMEOUT_SECONDS = config.oidcPollingTimeoutSeconds;
export default class ScopesScopeAuthenticateMethodOidcRoute extends Route {
// =services
@service session;
@service windowManager;
// =attributes
/**
* A simple Ember Concurrency-based polling task that refreshes the route
* every POLL_TIMEOUT_SECONDS seconds. This is necessary to display changes
* to session `status` that may occur.
*
* NOTE: tasks are sort of attributes and sort of methods, but they are not
* language-level constructs. Thus we annotate this task as if it
* is an attribute.
* @type {Task}
*/
@task(function * () {
while(!this.session.isAuthenticated) {
yield timeout(POLL_TIMEOUT_SECONDS * 1000);
yield this.refresh();
}
/* eslint-disable-next-line prettier/prettier */
}).drop() poller;
// =methods
beforeModel;
model() {
const scope = this.modelFor('scopes.scope');
const authMethod = this.modelFor('scopes.scope.authenticate.method');
@@ -47,18 +27,9 @@ export default class ScopesScopeAuthenticateMethodOidcRoute extends Route {
return oidc.attemptFetchToken({ scope, authMethod });
}
/**
* If authenticated, redirects to index for further processing.
*/
redirect() {
if (this.session.isAuthenticated) this.replaceWith('index');
}
/**
* When this route is activated (entered), begin polling for changes.
*/
activate() {
this.poller.perform();
@runEvery(POLL_TIMEOUT_SECONDS * 1000)
poller() {
this.refresh();
}
/**
@@ -66,7 +37,6 @@ export default class ScopesScopeAuthenticateMethodOidcRoute extends Route {
* any windows opened via the window manager service.
*/
deactivate() {
this.poller.cancelAll();
this.windowManager.closeAll();
}
@@ -81,5 +51,4 @@ export default class ScopesScopeAuthenticateMethodOidcRoute extends Route {
// rethrow the error to activate the notifyError decorator
throw e;
}
}
+40 -64
View File
@@ -2,8 +2,8 @@ import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { all, hash } from 'rsvp';
import { task, timeout } from 'ember-concurrency';
import { A } from '@ember/array';
import runEvery from 'ember-pollster/decorators/route/run-every';
import { notifySuccess, notifyError } from 'core/decorators/notify';
import config from '../../../config/environment';
import { resourceFilter } from 'core/decorators/resource-filter';
@@ -11,7 +11,6 @@ import { resourceFilter } from 'core/decorators/resource-filter';
const POLL_TIMEOUT_SECONDS = config.sessionPollingTimeoutSeconds;
export default class ScopesScopeSessionsRoute extends Route {
// =services
@service intl;
@@ -23,26 +22,14 @@ export default class ScopesScopeSessionsRoute extends Route {
@resourceFilter({
allowed: ['active', 'pending', 'canceling', 'terminated'],
defaultValue: ['active','pending', 'canceling']
}) status;
defaultValue: ['active', 'pending', 'canceling'],
})
status;
/**
* A simple Ember Concurrency-based polling task that refreshes the route
* every POLL_TIMEOUT_SECONDS seconds. This is necessary to display changes
* to session `status` that may occur.
*
* NOTE: tasks are sort of attributes and sort of methods, but they are not
* language-level constructs. Thus we annotate this task as if it
* is an attribute.
* @type {Task}
*/
@task(function * () {
while(true) {
yield timeout(POLL_TIMEOUT_SECONDS * 1000);
yield this.refresh();
}
/* eslint-disable-next-line prettier/prettier */
}).drop() poller;
@runEvery(POLL_TIMEOUT_SECONDS * 1000)
poller() {
return this.refresh();
}
// =methods
@@ -68,47 +55,36 @@ export default class ScopesScopeSessionsRoute extends Route {
);
const sessionAggregates = await all(
sessions.map(session => hash({
session,
user: session.user_id
? (
this.store.peekRecord('user', session.user_id) ||
sessions.map((session) =>
hash({
session,
user: session.user_id
? this.store.peekRecord('user', session.user_id) ||
this.store.findRecord('user', session.user_id)
)
: null,
target: session.target_id
? (
this.store.peekRecord('target', session.target_id) ||
: null,
target: session.target_id
? this.store.peekRecord('target', session.target_id) ||
this.store.findRecord('target', session.target_id)
)
: null,
}))
: null,
})
)
);
// Sort sessions by time created...
let sortedSessionAggregates =
A(sessionAggregates).sortBy('session.created_time').reverse();
let sortedSessionAggregates = A(sessionAggregates)
.sortBy('session.created_time')
.reverse();
// Then move active sessions to the top...
sortedSessionAggregates = [
...sortedSessionAggregates.filter((aggregate) => aggregate.session.status === 'active'),
...sortedSessionAggregates.filter((aggregate) => aggregate.session.status !== 'active'),
...sortedSessionAggregates.filter(
(aggregate) => aggregate.session.status === 'active'
),
...sortedSessionAggregates.filter(
(aggregate) => aggregate.session.status !== 'active'
),
];
return sortedSessionAggregates;
}
/**
* When this route is activated (entered), begin polling for changes.
*/
activate() {
this.poller.perform();
}
/**
* When this route is deactivated (exited), stop polling for changes.
*/
deactivate() {
this.poller.cancelAll();
}
// =actions
/**
@@ -122,21 +98,21 @@ export default class ScopesScopeSessionsRoute extends Route {
await session.cancelSession();
}
/**
/**
* Sets the specified resource filter field to the specified value.
* @param {string} field
* @param value
*/
@action
filterBy(field, value) {
this[field] = value;
}
@action
filterBy(field, value) {
this[field] = value;
}
/**
* Clears and filter selections.
*/
@action
clearAllFilters() {
this.status = [];
}
/**
* Clears and filter selections.
*/
@action
clearAllFilters() {
this.status = [];
}
}
-3
View File
@@ -151,9 +151,6 @@ module.exports = function (environment) {
// Notification timeout should be 0 for fast tests
ENV.notifyTimeout = 0;
// Polling timeouts should be short
ENV.sessionPollingTimeoutSeconds = 0.25;
ENV.oidcPollingTimeoutSeconds = 0;
ENV.enableConfirmService = false;
// Enable tests for development features
+2 -2
View File
@@ -35,7 +35,6 @@
"ember-named-blocks-polyfill": "^0.2.4"
},
"devDependencies": {
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
"@babel/core": "^7.16.5",
"@ember/optional-features": "^2.0.0",
"@ember/render-modifiers": "^1.0.2",
@@ -63,7 +62,6 @@
"ember-cli-sri": "^2.1.1",
"ember-cli-template-lint": "^2.0.2",
"ember-cli-terser": "^4.0.2",
"ember-concurrency": "^1.3.0",
"ember-exam": "^6.1.0",
"ember-export-application-global": "^2.0.1",
"ember-inline-svg": "^1.0.0",
@@ -72,6 +70,7 @@
"ember-modifier": "^2.1.2",
"ember-notify": "^6.0.1",
"ember-page-title": "^6.2.2",
"ember-pollster": "^0.0.1-beta.2",
"ember-qunit": "^5.1.4",
"ember-resolver": "^8.0.3",
"ember-source": "~3.28.4",
@@ -84,6 +83,7 @@
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-qunit": "^6.2.0",
"faker": "^5.5.3",
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
"loader.js": "^4.7.0",
"npm-run-all": "^4.1.5",
"prettier": "^2.4.0",
@@ -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 { run, later } from '@ember/runloop';
import { runAllJobs } from 'ember-pollster/test-support';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import {
currentSession,
@@ -383,6 +383,9 @@ module('Acceptance | authentication', function (hooks) {
);
await visit(authMethodOIDCAuthenticateURL);
await click('form [type="submit"]');
// explicitly poll multiple times to trigger mock OIDC auth
await runAllJobs();
await runAllJobs();
});
// TODO: test OIDC retry and cancel
+22 -41
View File
@@ -3,7 +3,6 @@ import { visit, currentURL, findAll, find, click } from '@ember/test-helpers';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import a11yAudit from 'ember-a11y-testing/test-support/audit';
import { run, later } from '@ember/runloop';
import { Response } from 'miragejs';
import {
authenticateSession,
@@ -81,45 +80,36 @@ module('Acceptance | sessions', function (hooks) {
test('visiting sessions', async function (assert) {
assert.expect(2);
authenticateSession({});
later(async () => {
run.cancelTimers();
await a11yAudit();
assert.equal(currentURL(), urls.sessions);
assert.equal(findAll('tbody tr').length, instances.sessions.length);
}, 750);
await visit(urls.sessions);
await a11yAudit();
assert.equal(currentURL(), urls.sessions);
assert.equal(findAll('tbody tr').length, instances.sessions.length);
});
test('Users cannot navigate to sessions without proper authorization', async function (assert) {
assert.expect(2);
instances.scopes.project.authorized_collection_actions.sessions = [];
authenticateSession({});
later(async () => {
run.cancelTimers();
assert.notOk(
instances.scopes.project.authorized_collection_actions.sessions.includes(
'list'
)
);
assert.notOk(find(`[href="${urls.sessions}"]`));
}, 750);
await visit(urls.orgScope);
assert.notOk(
instances.scopes.project.authorized_collection_actions.sessions.includes(
'list'
)
);
assert.notOk(find(`[href="${urls.sessions}"]`));
});
test('Users can navigate to sessions with proper authorization', async function (assert) {
assert.expect(2);
instances.scopes.project.authorized_collection_actions.sessions = ['list'];
authenticateSession({});
later(async () => {
run.cancelTimers();
assert.ok(
instances.scopes.project.authorized_collection_actions.sessions.includes(
'list'
)
);
assert.ok(find(`[href="${urls.sessions}"]`));
}, 750);
await visit(urls.orgScope);
assert.ok(
instances.scopes.project.authorized_collection_actions.sessions.includes(
'list'
)
);
assert.ok(find(`[href="${urls.sessions}"]`));
});
test('visiting sessions without users or targets is OK', async function (assert) {
@@ -129,37 +119,28 @@ module('Acceptance | sessions', function (hooks) {
targetId: null,
});
authenticateSession({});
later(async () => {
run.cancelTimers();
await a11yAudit();
assert.equal(currentURL(), urls.sessions);
assert.equal(findAll('tbody tr').length, instances.sessions.length);
}, 750);
await visit(urls.sessions);
await a11yAudit();
assert.equal(currentURL(), urls.sessions);
assert.equal(findAll('tbody tr').length, instances.sessions.length);
});
test('cancelling a session', async function (assert) {
assert.expect(2);
authenticateSession({});
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);
assert.equal(currentURL(), urls.sessions);
await click('tbody tr:first-child td:last-child button');
assert.ok(find('[role="alert"].is-success'));
});
test('cancelling a session with error shows notification', async function (assert) {
assert.expect(2);
this.server.post('/sessions/:id_method', () => new Response(400));
authenticateSession({});
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);
assert.equal(currentURL(), urls.sessions);
await click('tbody tr:first-child td:last-child button');
assert.ok(find('[role="alert"].is-error'));
});
});
@@ -1,39 +1,20 @@
import Route from '@ember/routing/route';
import { getOwner } from '@ember/application';
import { inject as service } from '@ember/service';
import { task, timeout } from 'ember-concurrency';
import { action } from '@ember/object';
import runEvery from 'ember-pollster/decorators/route/run-every';
import { notifyError } from 'core/decorators/notify';
import config from '../../../../../config/environment';
const POLL_TIMEOUT_SECONDS = config.oidcPollingTimeoutSeconds;
export default class ScopesScopeAuthenticateMethodOidcRoute extends Route {
// =services
@service session;
// =attributes
/**
* A simple Ember Concurrency-based polling task that refreshes the route
* every POLL_TIMEOUT_SECONDS seconds. This is necessary to display changes
* to session `status` that may occur.
*
* NOTE: tasks are sort of attributes and sort of methods, but they are not
* language-level constructs. Thus we annotate this task as if it
* is an attribute.
* @type {Task}
*/
@task(function * () {
while(!this.session.isAuthenticated) {
yield timeout(POLL_TIMEOUT_SECONDS * 1000);
yield this.refresh();
}
/* eslint-disable-next-line prettier/prettier */
}).drop() poller;
// =methods
model() {
@@ -46,25 +27,9 @@ export default class ScopesScopeAuthenticateMethodOidcRoute extends Route {
return oidc.attemptFetchToken({ scope, authMethod });
}
/**
* If authenticated, redirects to index for further processing.
*/
redirect() {
if (this.session.isAuthenticated) this.replaceWith('index');
}
/**
* When this route is activated (entered), begin polling for changes.
*/
activate() {
this.poller.perform();
}
/**
* When this route is deactivated (exited), stop polling for changes.
*/
deactivate() {
this.poller.cancelAll();
@runEvery(POLL_TIMEOUT_SECONDS * 1000)
poller() {
this.refresh();
}
/**
@@ -78,5 +43,4 @@ export default class ScopesScopeAuthenticateMethodOidcRoute extends Route {
// rethrow the error to activate the notifyError decorator
throw e;
}
}
@@ -1,15 +1,14 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { task, timeout } from 'ember-concurrency';
import config from '../../../../config/environment';
import { action } from '@ember/object';
import runEvery from 'ember-pollster/decorators/route/run-every';
import { notifySuccess, notifyError } from 'core/decorators/notify';
import { resourceFilter } from 'core/decorators/resource-filter';
import config from '../../../../config/environment';
const POLL_TIMEOUT_SECONDS = config.sessionPollingTimeoutSeconds;
export default class ScopesScopeProjectsSessionsRoute extends Route {
// =services
@service ipc;
@@ -20,32 +19,16 @@ export default class ScopesScopeProjectsSessionsRoute extends Route {
@resourceFilter({
allowed: ['active', 'pending', 'canceling', 'terminated'],
defaultValue: ['active','pending', 'canceling']
}) status;
defaultValue: ['active', 'pending', 'canceling'],
})
status;
@resourceFilter({
allowed: (route) => route.modelFor('scopes.scope.projects'),
serialize: ({ id }) => id,
findBySerialized: ({ id }, value) => id === value
}) project;
/**
* A simple Ember Concurrency-based polling task that refreshes the route
* every POLL_TIMEOUT_SECONDS seconds. This is necessary to display changes
* to session `status` that may occur.
*
* NOTE: tasks are sort of attributes and sort of methods, but they are not
* language-level constructs. Thus we annotate this task as if it
* is an attribute.
* @type {Task}
*/
@task(function * () {
while(true) {
yield timeout(POLL_TIMEOUT_SECONDS * 1000);
yield this.refresh();
}
/* eslint-disable-next-line prettier/prettier */
}).drop() poller;
findBySerialized: ({ id }, value) => id === value,
})
project;
// =methods
@@ -67,28 +50,23 @@ export default class ScopesScopeProjectsSessionsRoute extends Route {
const { user_id } = this.session.data.authenticated;
const projects = this.project || [];
await this.store.query('target', { recursive: true, scope_id });
return await this.resourceFilterStore.queryBy('session', {
user_id,
status,
scope_id: projects.map(({ id }) => id)
}, {
recursive: true,
scope_id
});
return await this.resourceFilterStore.queryBy(
'session',
{
user_id,
status,
scope_id: projects.map(({ id }) => id),
},
{
recursive: true,
scope_id,
}
);
}
/**
* When this route is activated (entered), begin polling for changes.
*/
activate() {
this.poller.perform();
}
/**
* When this route is deactivated (exited), stop polling for changes.
*/
deactivate() {
this.poller.cancelAll();
@runEvery(POLL_TIMEOUT_SECONDS * 1000)
poller() {
return this.refresh();
}
// =actions
@@ -1,14 +1,13 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';
import { task, timeout } from 'ember-concurrency';
import config from '../../../../config/environment';
import { resourceFilter } from 'core/decorators/resource-filter';
import runEvery from 'ember-pollster/decorators/route/run-every';
import config from '../../../../config/environment';
const POLL_TIMEOUT_SECONDS = config.sessionPollingTimeoutSeconds;
export default class ScopesScopeProjectsTargetsRoute extends Route {
// =services
@service ipc;
@@ -23,26 +22,9 @@ export default class ScopesScopeProjectsTargetsRoute extends Route {
@resourceFilter({
allowed: (route) => route.modelFor('scopes.scope.projects'),
serialize: ({ id }) => id,
findBySerialized: ({ id }, value) => id === value
}) project;
/**
* A simple Ember Concurrency-based polling task that refreshes the route
* every POLL_TIMEOUT_SECONDS seconds. This is necessary to display changes
* to session `status` that may occur.
*
* NOTE: tasks are sort of attributes and sort of methods, but they are not
* language-level constructs. Thus we annotate this task as if it
* is an attribute.
* @type {Task}
*/
@task(function * () {
while(true) {
yield timeout(POLL_TIMEOUT_SECONDS * 1000);
yield this.refresh();
}
/* eslint-disable-next-line prettier/prettier */
}).drop() poller;
findBySerialized: ({ id }, value) => id === value,
})
project;
// =methods
@@ -61,33 +43,30 @@ export default class ScopesScopeProjectsTargetsRoute extends Route {
const { id: scope_id } = this.modelFor('scopes.scope');
const { user_id } = this.session.data.authenticated;
const projects = this.project || [];
await this.resourceFilterStore.queryBy('session', { user_id }, {
recursive: true,
scope_id
});
return this.resourceFilterStore.queryBy('target', {
scope_id: projects.map(({ id }) => id),
authorized_actions: [
{ contains: 'authorize-session' },
]
}, {
recursive: true,
scope_id
});
await this.resourceFilterStore.queryBy(
'session',
{ user_id },
{
recursive: true,
scope_id,
}
);
return this.resourceFilterStore.queryBy(
'target',
{
scope_id: projects.map(({ id }) => id),
authorized_actions: [{ contains: 'authorize-session' }],
},
{
recursive: true,
scope_id,
}
);
}
/**
* When this route is activated (entered), begin polling for changes.
*/
activate() {
this.poller.perform();
}
/**
* When this route is deactivated (exited), stop polling for changes.
*/
deactivate() {
this.poller.cancelAll();
@runEvery(POLL_TIMEOUT_SECONDS * 1000)
poller() {
return this.refresh();
}
// =actions
@@ -112,7 +91,7 @@ export default class ScopesScopeProjectsTargetsRoute extends Route {
const options = {
target_id: model.id,
token: this.session.data.authenticated.token
token: this.session.data.authenticated.token,
};
if (host) options.host_id = host.id;
@@ -132,10 +111,11 @@ export default class ScopesScopeProjectsTargetsRoute extends Route {
session.proxy_address = address;
session.proxy_port = port;
if (credentials) {
credentials.forEach(cred => session.addCredential(cred));
credentials.forEach((cred) => session.addCredential(cred));
}
} catch(e) {
this.confirm.confirm(e.message, { isConnectError: true })
} catch (e) {
this.confirm
.confirm(e.message, { isConnectError: true })
// Retry
.then(() => this.connect(model))
.catch(() => null /* no op */);
+2 -2
View File
@@ -53,7 +53,6 @@
"ember-named-blocks-polyfill": "^0.2.4"
},
"devDependencies": {
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
"@doyensec/electronegativity": "^1.9.1",
"@ember/optional-features": "^2.0.0",
"@ember/render-modifiers": "^1.0.2",
@@ -83,7 +82,6 @@
"ember-cli-sri": "^2.1.1",
"ember-cli-template-lint": "^2.0.2",
"ember-cli-terser": "^4.0.2",
"ember-concurrency": "^1.3.0",
"ember-electron": "^3.0.0",
"ember-exam": "^6.1.0",
"ember-export-application-global": "^2.0.1",
@@ -93,6 +91,7 @@
"ember-modifier": "^2.1.2",
"ember-notify": "^6.0.1",
"ember-page-title": "^6.2.2",
"ember-pollster": "^0.0.1-beta.2",
"ember-qunit": "^5.1.4",
"ember-resolver": "^8.0.3",
"ember-source": "~3.28.4",
@@ -105,6 +104,7 @@
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-qunit": "^6.2.0",
"faker": "^5.5.3",
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
"loader.js": "^4.7.0",
"npm-run-all": "^4.1.5",
"playwright": "^1.15.0",
@@ -9,7 +9,6 @@ import {
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';
@@ -162,20 +161,17 @@ module('Acceptance | authentication', function (hooks) {
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"]');
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);
});
test('401 responses result in deauthentication', async function (assert) {
@@ -183,21 +179,18 @@ module('Acceptance | authentication', function (hooks) {
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"]');
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'
);
});
test('color theme is applied from session data', async function (assert) {
@@ -208,29 +201,25 @@ module('Acceptance | authentication', function (hooks) {
type: instances.scopes.global.type,
},
});
later(async () => {
run.cancelTimers();
// system default
assert.notOk(currentSession().get('data.theme'));
assert.notOk(getRootElement().classList.contains('rose-theme-light'));
assert.notOk(getRootElement().classList.contains('rose-theme-dark'));
// toggle light mode
await click('[name="theme"][value="light"]');
assert.equal(currentSession().get('data.theme'), 'light');
assert.ok(getRootElement().classList.contains('rose-theme-light'));
assert.notOk(getRootElement().classList.contains('rose-theme-dark'));
// toggle dark mode
await click('[name="theme"][value="dark"]');
assert.equal(currentSession().get('data.theme'), 'dark');
assert.notOk(getRootElement().classList.contains('rose-theme-light'));
assert.ok(getRootElement().classList.contains('rose-theme-dark'));
// toggle system default
await click('[name="theme"][value=""]');
assert.notOk(currentSession().get('data.theme'));
assert.notOk(getRootElement().classList.contains('rose-theme-light'));
assert.notOk(getRootElement().classList.contains('rose-theme-dark'));
}, 750);
await visit(urls.scopes.org);
// system default
assert.notOk(currentSession().get('data.theme'));
assert.notOk(getRootElement().classList.contains('rose-theme-light'));
assert.notOk(getRootElement().classList.contains('rose-theme-dark'));
// toggle light mode
await click('[name="theme"][value="light"]');
assert.equal(currentSession().get('data.theme'), 'light');
assert.ok(getRootElement().classList.contains('rose-theme-light'));
assert.notOk(getRootElement().classList.contains('rose-theme-dark'));
// toggle dark mode
await click('[name="theme"][value="dark"]');
assert.equal(currentSession().get('data.theme'), 'dark');
assert.notOk(getRootElement().classList.contains('rose-theme-light'));
assert.ok(getRootElement().classList.contains('rose-theme-dark'));
// toggle system default
await click('[name="theme"][value=""]');
assert.notOk(currentSession().get('data.theme'));
assert.notOk(getRootElement().classList.contains('rose-theme-light'));
assert.notOk(getRootElement().classList.contains('rose-theme-dark'));
});
});
@@ -9,7 +9,6 @@ import {
//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';
@@ -167,112 +166,79 @@ module('Acceptance | projects | sessions', function (hooks) {
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);
assert.equal(currentURL(), urls.sessions);
assert.equal(findAll('tbody tr').length, sessionsCount);
});
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);
await a11yAudit();
assert.ok(
find('.rose-message-title').textContent.trim(),
'No Sessions Available'
);
});
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);
await a11yAudit();
assert.equal(currentURL(), urls.sessions);
assert.equal(findAll('tbody tr').length, sessionsCount);
});
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);
assert.ok(
find('tbody tr:first-child th:first-child .session-status-active')
);
});
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);
assert.notOk(
find('tbody tr:first-child th:first-child .session-status-active')
);
});
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);
assert.notOk(
find('tbody tr:first-child th:first-child .session-status-active')
);
});
test('cancelling a session', async function (assert) {
assert.expect(1);
stubs.ipcService.withArgs('stop');
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);
await click('tbody tr:first-child td:last-child button');
assert.ok(find('[role="alert"].is-success'));
});
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);
await click('tbody tr:first-child td:last-child button');
assert.ok(find('[role="alert"].is-error'));
});
test('cancelling a session with ipc error shows notification', async function (assert) {
assert.expect(1);
stubs.ipcService.withArgs('stop').throws();
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);
await click('tbody tr:first-child td:last-child button');
assert.ok(find('[role="alert"].is-error'));
});
});
@@ -9,7 +9,6 @@ import {
//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';
@@ -174,52 +173,35 @@ module('Acceptance | projects | targets', function (hooks) {
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);
await a11yAudit();
assert.equal(currentURL(), urls.targets);
assert.equal(findAll('tbody tr').length, targetsCount);
});
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);
await click('tbody tr th a');
assert.equal(currentURL(), urls.sessions);
});
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);
assert.equal(
find('.rose-message-title').textContent.trim(),
'No Targets Available'
);
});
test('cannot navigate to a target without proper authorization', async function (assert) {
assert.expect(1);
instances.target.authorized_actions =
instances.target.authorized_actions.filter((item) => item !== 'read');
later(async () => {
run.cancelTimers();
assert.notOk(find('main tbody .rose-table-header-cell:nth-child(1) a'));
}, 750);
await visit(urls.targets);
assert.notOk(find('main tbody .rose-table-header-cell:nth-child(1) a'));
});
test('connecting to a target', async function (assert) {
@@ -233,26 +215,22 @@ module('Acceptance | projects | targets', function (hooks) {
});
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('.dialog-detail'), 'Success dialog');
assert.equal(findAll('.rose-dialog-footer button').length, 1);
assert.equal(
find('.rose-dialog-footer button').textContent.trim(),
'Close',
'Cannot retry'
);
assert.equal(
find('.rose-dialog-body .copyable-content').textContent.trim(),
'a_123:p_123'
);
}, 750);
await visit(urls.targets);
await click(
'tbody tr:first-child td:last-child button',
'Activate connect mode'
);
assert.ok(find('.dialog-detail'), 'Success dialog');
assert.equal(findAll('.rose-dialog-footer button').length, 1);
assert.equal(
find('.rose-dialog-footer button').textContent.trim(),
'Close',
'Cannot retry'
);
assert.equal(
find('.rose-dialog-body .copyable-content').textContent.trim(),
'a_123:p_123'
);
});
// Skipping because this test doesn't make sense. Users will never even see
@@ -269,12 +247,9 @@ module('Acceptance | projects | targets', function (hooks) {
assert.notOk(
instances.target.authorized_actions.includes('authorize-session')
);
later(async () => {
run.cancelTimers();
assert.ok(find('tbody tr:first-child'));
assert.notOk(find('tbody tr:first-child td:last-child button'));
}, 750);
await visit(urls.targets);
assert.ok(find('tbody tr:first-child'));
assert.notOk(find('tbody tr:first-child td:last-child button'));
});
test('handles cli error on connect', async function (assert) {
@@ -282,20 +257,16 @@ module('Acceptance | projects | targets', function (hooks) {
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);
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');
});
test('handles connect error', async function (assert) {
@@ -304,20 +275,16 @@ module('Acceptance | projects | targets', function (hooks) {
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);
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');
});
test('can retry on error', async function (assert) {
@@ -325,18 +292,14 @@ module('Acceptance | projects | targets', function (hooks) {
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);
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);
});
});
+144 -38
View File
@@ -5867,9 +5867,9 @@ agentkeepalive@^3.4.1:
humanize-ms "^1.2.1"
agentkeepalive@^4.1.3:
version "4.1.4"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b"
integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ==
version "4.2.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.2.0.tgz#616ce94ccb41d1a39a45d203d8076fe98713062d"
integrity sha512-0PhAp58jZNw13UJv7NVdTGb0ZcghHUb3DrZ046JiiJY/BOaTTpbwdHq2VObPCBV8M2GPh7sgrJ3AQ8Ey468LJw==
dependencies:
debug "^4.1.0"
depd "^1.1.2"
@@ -6500,6 +6500,11 @@ babel-helpers@^6.24.1:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
babel-import-util@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/babel-import-util/-/babel-import-util-0.2.0.tgz#b468bb679919601a3570f9e317536c54f2862e23"
integrity sha512-CtWYYHU/MgK88rxMrLfkD356dApswtR/kWZ/c6JifG1m10e7tBBrs/366dFzWMAoqYmG5/JSh+94tUSpIwh+ag==
babel-loader@^8.0.6, babel-loader@^8.2.2:
version "8.2.2"
resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.2.tgz#9363ce84c10c9a40e6c753748e1441b60c8a0b81"
@@ -6560,6 +6565,16 @@ babel-plugin-ember-modules-api-polyfill@^3.5.0:
dependencies:
ember-rfc176-data "^0.3.17"
babel-plugin-ember-template-compilation@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/babel-plugin-ember-template-compilation/-/babel-plugin-ember-template-compilation-1.0.1.tgz#64baf434ff1b751c6292936f8b9eb75a2f8149dc"
integrity sha512-V/kY6CDyUNrl5Kx6UPKUPhzSKNfdrxNii+S5zK4dgJvVyoxFv7Ykg06Ct/yskY0LkA4wUPdYN7JOBtYJwHk2sg==
dependencies:
babel-import-util "^0.2.0"
line-column "^1.0.2"
magic-string "^0.25.7"
string.prototype.matchall "^4.0.5"
babel-plugin-emotion@^10.0.27:
version "10.2.2"
resolved "https://registry.yarnpkg.com/babel-plugin-emotion/-/babel-plugin-emotion-10.2.2.tgz#a1fe3503cff80abfd0bdda14abd2e8e57a79d17d"
@@ -6607,6 +6622,17 @@ babel-plugin-htmlbars-inline-precompile@^5.0.0:
parse-static-imports "^1.1.0"
string.prototype.matchall "^4.0.4"
babel-plugin-htmlbars-inline-precompile@^5.3.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/babel-plugin-htmlbars-inline-precompile/-/babel-plugin-htmlbars-inline-precompile-5.3.1.tgz#5ba272e2e4b6221522401f5f1d98a73b1de38787"
integrity sha512-QWjjFgSKtSRIcsBhJmEwS2laIdrA6na8HAlc/pEAhjHgQsah/gMiBFRZvbQTy//hWxR4BMwV7/Mya7q5H8uHeA==
dependencies:
babel-plugin-ember-modules-api-polyfill "^3.5.0"
line-column "^1.0.2"
magic-string "^0.25.7"
parse-static-imports "^1.1.0"
string.prototype.matchall "^4.0.5"
babel-plugin-istanbul@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765"
@@ -7798,12 +7824,12 @@ browserify-zlib@^0.2.0:
pako "~1.0.5"
browserslist@4.14.2, browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.5, browserslist@^4.16.6, browserslist@^4.16.7, browserslist@^4.17.3, browserslist@^4.17.5, browserslist@^4.19.1:
version "4.17.6"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.6.tgz#c76be33e7786b497f66cad25a73756c8b938985d"
integrity sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==
version "4.19.1"
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3"
integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==
dependencies:
caniuse-lite "^1.0.30001274"
electron-to-chromium "^1.3.886"
caniuse-lite "^1.0.30001286"
electron-to-chromium "^1.4.17"
escalade "^3.1.1"
node-releases "^2.0.1"
picocolors "^1.0.0"
@@ -8116,10 +8142,10 @@ caniuse-api@^3.0.0:
lodash.memoize "^4.1.2"
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001274:
version "1.0.30001279"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz#eb06818da481ef5096a3b3760f43e5382ed6b0ce"
integrity sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ==
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001286:
version "1.0.30001295"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001295.tgz#68a60f8f0664f342b2835c5d8898b4faea7b3d51"
integrity sha512-lSP16vcyC0FEy0R4ECc9duSPoKoZy+YkpGkue9G4D81OfPnliopaZrU10+qtPdT8PbGXad/PNx43TIQrOmJZSQ==
capture-exit@^2.0.0:
version "2.0.0"
@@ -9964,10 +9990,10 @@ electron-rebuild@^2.0.3:
tar "^6.0.5"
yargs "^16.0.0"
electron-to-chromium@^1.3.886:
version "1.3.895"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.895.tgz#9b0f8f2e32d8283bbb200156fd5d8dfd775f31ed"
integrity sha512-9Ww3fB8CWctjqHwkOt7DQbMZMpal2x2reod+/lU4b9axO1XJEDUpPMBxs7YnjLhhqpKXIIB5SRYN/B4K0QpvyQ==
electron-to-chromium@^1.4.17:
version "1.4.31"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.31.tgz#8d5ccc3f8253cd142b07afaa84f200fd33a7f2a6"
integrity sha512-t3XVQtk+Frkv6aTD4RRk0OqosU+VLe1dQFW83MDer78ZD6a52frgXuYOIsLYTQiH2Lm+JB2OKYcn7zrX+YGAiQ==
element-resize-detector@^1.2.2:
version "1.2.3"
@@ -10275,6 +10301,27 @@ ember-cli-htmlbars@^5.3.1, ember-cli-htmlbars@^5.3.2, ember-cli-htmlbars@^5.7.1:
strip-bom "^4.0.0"
walk-sync "^2.2.0"
ember-cli-htmlbars@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/ember-cli-htmlbars/-/ember-cli-htmlbars-6.0.1.tgz#5487831d477e61682bc867fd138808269e5d2152"
integrity sha512-IDsl9uty+MXtMfp/BUTEc/Q36EmlHYj8ZdPekcoRa8hmdsigHnK4iokfaB7dJFktlf6luruei+imv7JrJrBQPQ==
dependencies:
"@ember/edition-utils" "^1.2.0"
babel-plugin-ember-template-compilation "^1.0.0"
babel-plugin-htmlbars-inline-precompile "^5.3.0"
broccoli-debug "^0.6.5"
broccoli-persistent-filter "^3.1.2"
broccoli-plugin "^4.0.3"
ember-cli-version-checker "^5.1.2"
fs-tree-diff "^2.0.1"
hash-for-dep "^1.5.1"
heimdalljs-logger "^0.1.10"
json-stable-stringify "^1.0.1"
semver "^7.3.4"
silent-error "^1.1.1"
strip-bom "^4.0.0"
walk-sync "^2.2.0"
ember-cli-inject-live-reload@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/ember-cli-inject-live-reload/-/ember-cli-inject-live-reload-2.1.0.tgz#ef63c733c133024d5726405a3c247fa12e88a385"
@@ -10630,15 +10677,6 @@ ember-concurrency@^0.10.0:
ember-compatibility-helpers "^1.2.0"
ember-maybe-import-regenerator "^0.1.5"
ember-concurrency@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/ember-concurrency/-/ember-concurrency-1.3.0.tgz#66f90fb792687470bcee1172adc0ebf33f5e8b9c"
integrity sha512-DwGlfWFpYyAkTwsedlEtK4t1DznJSculAW6Vq5S1C0shVPc5b6tTpHB2FFYisannSYkm+wpm1f1Pd40qiNPtOQ==
dependencies:
ember-cli-babel "^7.7.3"
ember-compatibility-helpers "^1.2.0"
ember-maybe-import-regenerator "^0.1.6"
ember-cookies@^0.5.0, ember-cookies@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/ember-cookies/-/ember-cookies-0.5.2.tgz#06e33463f2f83254fefaf224cc944dec3fb9d3ba"
@@ -10896,7 +10934,7 @@ ember-loading@^0.4.0:
ember-concurrency "^0.10.0"
ember-decorators-polyfill "^1.1.5"
ember-maybe-import-regenerator@^0.1.5, ember-maybe-import-regenerator@^0.1.6:
ember-maybe-import-regenerator@^0.1.5:
version "0.1.6"
resolved "https://registry.yarnpkg.com/ember-maybe-import-regenerator/-/ember-maybe-import-regenerator-0.1.6.tgz#35d41828afa6d6a59bc0da3ce47f34c573d776ca"
integrity sha1-NdQYKK+m1qWbwNo85H80xXPXdso=
@@ -10973,6 +11011,14 @@ ember-page-title@^6.2.2:
dependencies:
ember-cli-babel "^7.23.1"
ember-pollster@^0.0.1-beta.2:
version "0.0.1-beta.2"
resolved "https://registry.yarnpkg.com/ember-pollster/-/ember-pollster-0.0.1-beta.2.tgz#07b8a49683cfb04e33a60713fb044e07a0a8f330"
integrity sha512-vnWBzPIJ0NU6065OLgLd84Snalfxi5Z606wiDSMnW1oSl20D3cAsGEwmpujZsfzlof7Y2McE/ghNjMBKYveyIQ==
dependencies:
ember-cli-babel "^7.26.8"
ember-cli-htmlbars "^6.0.1"
ember-qunit@^5.1.4:
version "5.1.4"
resolved "https://registry.yarnpkg.com/ember-qunit/-/ember-qunit-5.1.4.tgz#bc69f963a0f5409ce33bee1e4d8146b1407147bf"
@@ -11414,6 +11460,32 @@ es-abstract@^1.17.0-next.0, es-abstract@^1.17.2, es-abstract@^1.18.0-next.1, es-
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.1"
es-abstract@^1.19.1:
version "1.19.1"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.1.tgz#d4885796876916959de78edaa0df456627115ec3"
integrity sha512-2vJ6tjA/UfqLm2MPs7jxVybLoB8i1t1Jd9R3kISld20sIxPcTbLuggQOUxeWeAvIUkduv/CfMjuh4WmiXr2v9w==
dependencies:
call-bind "^1.0.2"
es-to-primitive "^1.2.1"
function-bind "^1.1.1"
get-intrinsic "^1.1.1"
get-symbol-description "^1.0.0"
has "^1.0.3"
has-symbols "^1.0.2"
internal-slot "^1.0.3"
is-callable "^1.2.4"
is-negative-zero "^2.0.1"
is-regex "^1.1.4"
is-shared-array-buffer "^1.0.1"
is-string "^1.0.7"
is-weakref "^1.0.1"
object-inspect "^1.11.0"
object-keys "^1.1.1"
object.assign "^4.1.2"
string.prototype.trimend "^1.0.4"
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.1"
es-array-method-boxes-properly@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e"
@@ -13111,6 +13183,14 @@ get-stream@^6.0.0:
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
get-symbol-description@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6"
integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==
dependencies:
call-bind "^1.0.2"
get-intrinsic "^1.1.1"
get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -14193,9 +14273,9 @@ ignore@^5.1.1, ignore@^5.1.4, ignore@^5.1.8:
integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==
immer@8.0.1, immer@^9.0.6:
version "9.0.6"
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.6.tgz#7a96bf2674d06c8143e327cbf73539388ddf1a73"
integrity sha512-G95ivKpy+EvVAnAab4fVa4YGYn24J1SpEktnJX7JJ45Bd7xqME/SCplFzYFmTbrkwZbQ4xJK1xMTUYBkN6pWsQ==
version "9.0.7"
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.7.tgz#b6156bd7db55db7abc73fd2fdadf4e579a701075"
integrity sha512-KGllzpbamZDvOIxnmJ0jI840g7Oikx58lBPWV0hUh7dtAyZpFqqrBZdKka5GlTwMTZ1Tjc/bKKW4VSFAt6BqMA==
import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1:
version "3.3.0"
@@ -14530,7 +14610,7 @@ is-buffer@^2.0.0:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191"
integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==
is-callable@^1.1.4, is-callable@^1.2.3:
is-callable@^1.1.4, is-callable@^1.2.3, is-callable@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
@@ -14862,7 +14942,7 @@ is-reference@^1.1.0:
dependencies:
"@types/estree" "*"
is-regex@^1.1.2, is-regex@^1.1.3:
is-regex@^1.1.2, is-regex@^1.1.3, is-regex@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
@@ -14895,6 +14975,11 @@ is-set@^2.0.2:
resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec"
integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==
is-shared-array-buffer@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6"
integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==
is-stream@^1.0.0, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@@ -14905,7 +14990,7 @@ is-stream@^2.0.0:
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
is-string@^1.0.5, is-string@^1.0.6:
is-string@^1.0.5, is-string@^1.0.6, is-string@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
@@ -14941,6 +15026,13 @@ is-utf8@^0.2.0, is-utf8@^0.2.1:
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
is-weakref@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
dependencies:
call-bind "^1.0.2"
is-whitespace-character@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.4.tgz#0858edd94a95594c7c9dd0b5c174ec6e45ee4aa7"
@@ -17073,9 +17165,9 @@ minipass@^3.0.0, minipass@^3.1.1:
yallist "^4.0.0"
minipass@^3.1.0, minipass@^3.1.3:
version "3.1.5"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.5.tgz#71f6251b0a33a49c01b3cf97ff77eda030dff732"
integrity sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw==
version "3.1.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.6.tgz#3b8150aa688a711a1521af5e8779c1d3bb4f45ee"
integrity sha512-rty5kpw9/z8SX9dmxblFA6edItUmwJgMeYDZRrwlIVN27i8gysGbznJwUggw2V/FVqFSDdWy040ZPS811DYAqQ==
dependencies:
yallist "^4.0.0"
@@ -21540,6 +21632,20 @@ string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2
regexp.prototype.flags "^1.3.1"
side-channel "^1.0.4"
string.prototype.matchall@^4.0.5:
version "4.0.6"
resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.6.tgz#5abb5dabc94c7b0ea2380f65ba610b3a544b15fa"
integrity sha512-6WgDX8HmQqvEd7J+G6VtAahhsQIssiZ8zl7zKh1VDMFyL3hRTJP4FTNA3RbIp2TOQ9AYNDcc7e3fH0Qbup+DBg==
dependencies:
call-bind "^1.0.2"
define-properties "^1.1.3"
es-abstract "^1.19.1"
get-intrinsic "^1.1.1"
has-symbols "^1.0.2"
internal-slot "^1.0.3"
regexp.prototype.flags "^1.3.1"
side-channel "^1.0.4"
string.prototype.padend@^3.0.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.2.tgz#6858ca4f35c5268ebd5e8615e1327d55f59ee311"
@@ -22649,9 +22755,9 @@ underscore.string@^3.2.2, underscore.string@~3.3.4:
util-deprecate "^1.0.2"
underscore@>=1.8.3, underscore@^1.12.1, underscore@~1.12.1:
version "1.13.1"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1"
integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==
version "1.13.2"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.2.tgz#276cea1e8b9722a8dbed0100a407dda572125881"
integrity sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==
unfetch@^4.2.0:
version "4.2.0"