From 8605a5422148f50da33059be7b76ff13ad5f530f Mon Sep 17 00:00:00 2001
From: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com>
Date: Tue, 17 Sep 2024 16:36:47 -0700
Subject: [PATCH] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20remove=20route-actio?=
=?UTF-8?q?n=20usage=20for=20scopes=20in=20desktop=20(#2447)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* refactor: 💡 remove route-action usage for scopes in desktop
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12621
---
ui/desktop/app/controllers/application.js | 38 +++++++++
ui/desktop/app/controllers/cluster-url.js | 34 ++++++++
ui/desktop/app/controllers/error.js | 10 +++
.../scopes/scope/authenticate/method/index.js | 81 +++++++++++++++++++
.../scopes/scope/authenticate/method/oidc.js | 2 +-
.../scopes/scope/projects/error.js | 10 +++
ui/desktop/app/routes/application.js | 33 --------
ui/desktop/app/routes/cluster-url.js | 30 +------
.../scopes/scope/authenticate/method.js | 66 ---------------
ui/desktop/app/templates/application.hbs | 8 +-
ui/desktop/app/templates/cluster-url.hbs | 2 +-
ui/desktop/app/templates/error.hbs | 2 +-
.../scope/authenticate/method/index.hbs | 2 +-
.../templates/scopes/scope/projects/error.hbs | 2 +-
ui/desktop/tests/helpers/window-mock-ipc.js | 6 +-
.../unit/controllers/application-test.js | 48 ++++++++++-
.../unit/controllers/cluster-url-test.js | 46 +++++++++++
.../tests/unit/controllers/error-test.js | 17 ++++
.../scope/authenticate/method/index-test.js | 79 ++++++++++++++++++
.../scope/authenticate/method/oidc-test.js | 1 -
.../scopes/scope/projects/error-test.js | 19 +++++
21 files changed, 395 insertions(+), 141 deletions(-)
create mode 100644 ui/desktop/app/controllers/cluster-url.js
create mode 100644 ui/desktop/app/controllers/error.js
create mode 100644 ui/desktop/app/controllers/scopes/scope/authenticate/method/index.js
create mode 100644 ui/desktop/app/controllers/scopes/scope/projects/error.js
create mode 100644 ui/desktop/tests/unit/controllers/cluster-url-test.js
create mode 100644 ui/desktop/tests/unit/controllers/error-test.js
create mode 100644 ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/index-test.js
create mode 100644 ui/desktop/tests/unit/controllers/scopes/scope/projects/error-test.js
diff --git a/ui/desktop/app/controllers/application.js b/ui/desktop/app/controllers/application.js
index 4f5936263..511049c93 100644
--- a/ui/desktop/app/controllers/application.js
+++ b/ui/desktop/app/controllers/application.js
@@ -5,10 +5,48 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
+import { action } from '@ember/object';
export default class ApplicationController extends Controller {
// =services
+ @service ipc;
@service session;
+ @service clusterUrl;
@service flashMessages;
+
+ // =actions
+
+ /**
+ * Delegates invalidation to the session service.
+ */
+ @action
+ invalidateSession() {
+ this.session.invalidate();
+ }
+
+ /**
+ * Disconnects from clusterUrl and invalidates session, thereby resetting
+ * the client and reloading to the onboarding clusterUrl screen.
+ */
+ @action
+ disconnect() {
+ this.clusterUrl.resetClusterUrl();
+ this.invalidateSession();
+ }
+
+ @action
+ minimize() {
+ this.ipc.invoke('minimizeWindow');
+ }
+
+ @action
+ toggleFullScreen() {
+ this.ipc.invoke('toggleFullscreenWindow');
+ }
+
+ @action
+ close() {
+ this.ipc.invoke('closeWindow');
+ }
}
diff --git a/ui/desktop/app/controllers/cluster-url.js b/ui/desktop/app/controllers/cluster-url.js
new file mode 100644
index 000000000..6da120b79
--- /dev/null
+++ b/ui/desktop/app/controllers/cluster-url.js
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import Controller from '@ember/controller';
+import { inject as service } from '@ember/service';
+import { action } from '@ember/object';
+import { loading } from 'ember-loading';
+import { notifyError } from 'core/decorators/notify';
+
+export default class ClusterUrlController extends Controller {
+ // =services
+
+ @service router;
+ @service('cluster-url') clusterUrlService;
+
+ // =actions
+
+ /**
+ * Points the API to the specified clusterUrl. When the main process receives
+ * the clusterUrl, it is expected that the renderer will be restarted.
+ * @param {string} clusterUrl
+ */
+ @action
+ @loading
+ @notifyError(() => 'errors.cluster-url-verification-failed.description', {
+ catch: true,
+ })
+ async setClusterUrl(clusterUrl) {
+ await this.clusterUrlService.setClusterUrl(clusterUrl);
+ this.router.replaceWith('index');
+ }
+}
diff --git a/ui/desktop/app/controllers/error.js b/ui/desktop/app/controllers/error.js
new file mode 100644
index 000000000..b22f8e996
--- /dev/null
+++ b/ui/desktop/app/controllers/error.js
@@ -0,0 +1,10 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import Controller, { inject as controller } from '@ember/controller';
+
+export default class ErrorController extends Controller {
+ @controller('application') appUtilities;
+}
diff --git a/ui/desktop/app/controllers/scopes/scope/authenticate/method/index.js b/ui/desktop/app/controllers/scopes/scope/authenticate/method/index.js
new file mode 100644
index 000000000..de28152b3
--- /dev/null
+++ b/ui/desktop/app/controllers/scopes/scope/authenticate/method/index.js
@@ -0,0 +1,81 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import Controller from '@ember/controller';
+import { inject as service } from '@ember/service';
+import { getOwner } from '@ember/application';
+import { action } from '@ember/object';
+import { loading } from 'ember-loading';
+import { notifyError } from 'core/decorators/notify';
+
+export default class ScopesScopeAuthenticateMethodIndexController extends Controller {
+ // =services
+
+ @service session;
+ @service ipc;
+ @service intl;
+ @service router;
+
+ // =methods
+
+ /**
+ * Kicks off the OIDC auth flow by opening a new window to the provider.
+ * @param {string} authenticatorName
+ * @param {object} options
+ */
+ async startOIDCAuthentication(authenticatorName, options) {
+ const oidc = getOwner(this).lookup(authenticatorName);
+ // TODO: delegate this call from the session service so that we don't have
+ // to look up the authenticator directly
+ const json = await oidc.startAuthentication(options);
+ await this.openExternalOIDCFlow(json.attributes.auth_url);
+ }
+
+ /**
+ * Opens the specified URL in a new tab or window. By default this uses
+ * `window.open`, but may be overridden.
+ * @param {string} url
+ */
+ async openExternalOIDCFlow(url) {
+ await this.ipc.invoke('openExternal', url);
+ }
+
+ // =actions
+
+ /**
+ * Delegates authentication to an authenticator of type matching the current
+ * auth method. If authentication fails, notifies with an alert.
+ */
+ @action
+ @loading
+ @notifyError(() => 'errors.authentication-failed.title', {
+ sticky: false,
+ catch: true,
+ })
+ async authenticate(authMethod, creds) {
+ const scope = authMethod.scope;
+ const authenticatorName = `authenticator:${authMethod.type}`;
+ const requestCookies = false;
+ switch (authMethod.type) {
+ case 'password':
+ case 'ldap':
+ await this.session.authenticate(
+ authenticatorName,
+ creds,
+ requestCookies,
+ { scope, authMethod },
+ );
+ this.router.transitionTo('index');
+ break;
+ case 'oidc':
+ await this.startOIDCAuthentication(authenticatorName, {
+ scope,
+ authMethod,
+ });
+ this.router.transitionTo('scopes.scope.authenticate.method.oidc');
+ break;
+ }
+ }
+}
diff --git a/ui/desktop/app/controllers/scopes/scope/authenticate/method/oidc.js b/ui/desktop/app/controllers/scopes/scope/authenticate/method/oidc.js
index c34971a14..c64b230c3 100644
--- a/ui/desktop/app/controllers/scopes/scope/authenticate/method/oidc.js
+++ b/ui/desktop/app/controllers/scopes/scope/authenticate/method/oidc.js
@@ -14,7 +14,7 @@ export default class ScopesScopeAuthenticateMethodOidcController extends Control
// =attributes
/**
- * Authetnication URL for the pending OIDC flow, if any.
+ * Authentication URL for the pending OIDC flow, if any.
* @type {?string}
*/
get authURL() {
diff --git a/ui/desktop/app/controllers/scopes/scope/projects/error.js b/ui/desktop/app/controllers/scopes/scope/projects/error.js
new file mode 100644
index 000000000..41b736690
--- /dev/null
+++ b/ui/desktop/app/controllers/scopes/scope/projects/error.js
@@ -0,0 +1,10 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import Controller, { inject as controller } from '@ember/controller';
+
+export default class ScopesScopeProjectsErrorController extends Controller {
+ @controller('application') appUtilities;
+}
diff --git a/ui/desktop/app/routes/application.js b/ui/desktop/app/routes/application.js
index 176429211..4d9e491b4 100644
--- a/ui/desktop/app/routes/application.js
+++ b/ui/desktop/app/routes/application.js
@@ -61,24 +61,6 @@ export default class ApplicationRoute extends Route {
// =actions
- /**
- * Delegates invalidation to the session service.
- */
- @action
- invalidateSession() {
- this.session.invalidate();
- }
-
- /**
- * Disconnects from clusterUrl and invalidates session, thereby resetting
- * the client and reloading to the onboarding clusterUrl screen.
- */
- @action
- disconnect() {
- this.clusterUrl.resetClusterUrl();
- this.invalidateSession();
- }
-
/**
* Invalidates the session if a 401 error occurs and returns false to
* prevent further error handling.
@@ -121,19 +103,4 @@ export default class ApplicationRoute extends Route {
rootEl.classList.remove('rose-theme-light');
}
}
-
- @action
- minimize() {
- this.ipc.invoke('minimizeWindow');
- }
-
- @action
- toggleFullScreen() {
- this.ipc.invoke('toggleFullscreenWindow');
- }
-
- @action
- close() {
- this.ipc.invoke('closeWindow');
- }
}
diff --git a/ui/desktop/app/routes/cluster-url.js b/ui/desktop/app/routes/cluster-url.js
index dc0576f8d..3e8985eb5 100644
--- a/ui/desktop/app/routes/cluster-url.js
+++ b/ui/desktop/app/routes/cluster-url.js
@@ -5,8 +5,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
-import { action } from '@ember/object';
-import { loading } from 'ember-loading';
import config from '../config/environment';
export default class ClusterUrlRoute extends Route {
@@ -47,37 +45,13 @@ export default class ClusterUrlRoute extends Route {
super.setupController(...arguments);
let clusterUrl = this.clusterUrl.rendererClusterUrl;
// If clusterUrl is unset and this is a development environment,
- // autoset the clusterUrl field of the UI for better DX.
+ // auto-set the clusterUrl field of the UI for better DX.
// The controller URL is almost always the same as the current window when
// using mocks, and this makes development more rapid since developers
// do not need to fill a clusterUrl on every session.
if (!clusterUrl && config.autoOrigin) {
clusterUrl = this.window.location.origin;
}
- controller.setProperties({ clusterUrl: clusterUrl });
- }
-
- /**
- * Points the API to the specified clusterUrl. When the main process receives
- * the clusterUrl, it is expected that the renderer will be restarted.
- * @param {string} clusterUrl
- */
- @action
- @loading
- async setClusterUrl(clusterUrl) {
- try {
- await this.clusterUrl.setClusterUrl(clusterUrl);
- this.router.replaceWith('index');
- } catch (e) {
- // If scopes do not load, we assume this is not a Boundary API
- const errorMessage = this.intl.t(
- 'errors.cluster-url-verification-failed.description',
- );
- this.flashMessages.danger(errorMessage, {
- notificationType: 'error',
- sticky: true,
- dismiss: (flash) => flash.destroyMessage(),
- });
- }
+ controller.setProperties({ clusterUrl });
}
}
diff --git a/ui/desktop/app/routes/scopes/scope/authenticate/method.js b/ui/desktop/app/routes/scopes/scope/authenticate/method.js
index 9f39de8b3..20070347c 100644
--- a/ui/desktop/app/routes/scopes/scope/authenticate/method.js
+++ b/ui/desktop/app/routes/scopes/scope/authenticate/method.js
@@ -5,19 +5,11 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
-import { getOwner } from '@ember/application';
-import { action } from '@ember/object';
-import { loading } from 'ember-loading';
-import { notifyError } from 'core/decorators/notify';
export default class ScopesScopeAuthenticateMethodRoute extends Route {
// =services
@service store;
- @service session;
- @service ipc;
- @service intl;
- @service router;
// =methods
@@ -30,62 +22,4 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route {
const adapterOptions = { scopeID: this.modelFor('scopes.scope').id };
return this.store.peekRecord('auth-method', id, adapterOptions);
}
-
- /**
- *
- */
- async startOIDCAuthentication(authenticatorName, options) {
- const oidc = getOwner(this).lookup(authenticatorName);
- // TODO: delegate this call from the session service so that we don't have
- // to look up the authenticator directly
- const json = await oidc.startAuthentication(options);
- await this.openExternalOIDCFlow(json.attributes.auth_url);
- }
-
- /**
- * Opens the specified URL in a new tab or window. By default this uses
- * `window.open`, but may be overriden.
- * @param {string} url
- */
- async openExternalOIDCFlow(url) {
- await this.ipc.invoke('openExternal', url);
- }
-
- // =actions
-
- /**
- * Delegates authentication to an authenticator of type matching the current
- * auth method. If authentication fails, notifies with an alert.
- */
- @action
- @loading
- @notifyError(() => 'errors.authentication-failed.title', {
- sticky: false,
- catch: true,
- })
- async authenticate(creds) {
- const scope = this.modelFor('scopes.scope');
- const authMethod = this.modelFor('scopes.scope.authenticate.method');
- const authenticatorName = `authenticator:${authMethod.type}`;
- const requestCookies = false;
- switch (authMethod.type) {
- case 'password':
- case 'ldap':
- await this.session.authenticate(
- authenticatorName,
- creds,
- requestCookies,
- { scope, authMethod },
- );
- this.router.transitionTo('index');
- break;
- case 'oidc':
- await this.startOIDCAuthentication(authenticatorName, {
- scope,
- authMethod,
- });
- this.router.transitionTo('scopes.scope.authenticate.method.oidc');
- break;
- }
- }
}
diff --git a/ui/desktop/app/templates/application.hbs b/ui/desktop/app/templates/application.hbs
index 37908434b..3b4913ed3 100644
--- a/ui/desktop/app/templates/application.hbs
+++ b/ui/desktop/app/templates/application.hbs
@@ -26,21 +26,21 @@
@style='warning'
class='button-window-close'
title={{t 'actions.close'}}
- {{on 'click' (route-action 'close')}}
+ {{on 'click' this.close}}
/>
{{/if}}
@@ -50,7 +50,7 @@
@icon='flight-icons/svg/user-circle-16'
as |dropdown|
>
-
+
{{t 'actions.deauthenticate'}}
diff --git a/ui/desktop/app/templates/cluster-url.hbs b/ui/desktop/app/templates/cluster-url.hbs
index 56fa81b6c..91f9975e0 100644
--- a/ui/desktop/app/templates/cluster-url.hbs
+++ b/ui/desktop/app/templates/cluster-url.hbs
@@ -10,7 +10,7 @@
>
-
+
{{t 'actions.disconnect'}}
diff --git a/ui/desktop/app/templates/scopes/scope/authenticate/method/index.hbs b/ui/desktop/app/templates/scopes/scope/authenticate/method/index.hbs
index 59c42f585..4b6dd00c0 100644
--- a/ui/desktop/app/templates/scopes/scope/authenticate/method/index.hbs
+++ b/ui/desktop/app/templates/scopes/scope/authenticate/method/index.hbs
@@ -5,6 +5,6 @@
\ No newline at end of file
diff --git a/ui/desktop/app/templates/scopes/scope/projects/error.hbs b/ui/desktop/app/templates/scopes/scope/projects/error.hbs
index 2d5250b08..169983495 100644
--- a/ui/desktop/app/templates/scopes/scope/projects/error.hbs
+++ b/ui/desktop/app/templates/scopes/scope/projects/error.hbs
@@ -21,7 +21,7 @@
-
+
{{t 'actions.disconnect'}}
diff --git a/ui/desktop/tests/helpers/window-mock-ipc.js b/ui/desktop/tests/helpers/window-mock-ipc.js
index 6cff14ed8..5f1244e29 100644
--- a/ui/desktop/tests/helpers/window-mock-ipc.js
+++ b/ui/desktop/tests/helpers/window-mock-ipc.js
@@ -21,7 +21,9 @@ class MockIPC {
return this.clusterUrl;
}
- resetClusterUrl() {}
+ resetClusterUrl() {
+ this.clusterUrl = null;
+ }
hasMacOSChrome() {}
showWindowActions() {}
addTokenToDaemons() {}
@@ -40,7 +42,7 @@ class MockIPC {
* to support the IPC mechanism.
*
* This mock is necessary to guarantee synchronous and deterministic
- * operation. The window's own API is evented and asychronous, which
+ * operation. The window's own API is evented and asynchronous, which
* cannot be relied on for testing purposes.
*
* @todo refactor mock postMessage and MessageChannel for reusability
diff --git a/ui/desktop/tests/unit/controllers/application-test.js b/ui/desktop/tests/unit/controllers/application-test.js
index bf49338b2..2c5ef8593 100644
--- a/ui/desktop/tests/unit/controllers/application-test.js
+++ b/ui/desktop/tests/unit/controllers/application-test.js
@@ -5,13 +5,57 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
+import { authenticateSession } from 'ember-simple-auth/test-support';
+import WindowMockIPC from '../../helpers/window-mock-ipc';
module('Unit | Controller | application', function (hooks) {
setupTest(hooks);
- // TODO: Replace this with your real tests.
+ let controller;
+ let session;
+ let clusterUrl;
+ let mockIPC;
+
+ const setupMockIpc = (test) => {
+ test.owner.register('service:browser/window', WindowMockIPC);
+ mockIPC = test.owner.lookup('service:browser/window').mockIPC;
+ };
+
+ hooks.beforeEach(async function () {
+ authenticateSession({});
+ controller = this.owner.lookup('controller:application');
+ session = this.owner.lookup('service:session');
+ clusterUrl = this.owner.lookup('service:cluster-url');
+ setupMockIpc(this);
+ });
+
test('it exists', function (assert) {
- let controller = this.owner.lookup('controller:application');
assert.ok(controller);
+ assert.ok(controller.minimize);
+ assert.ok(controller.toggleFullScreen);
+ assert.ok(controller.close);
+ });
+
+ test('invalidateSession action de-authenticates a user', async function (assert) {
+ assert.true(session.isAuthenticated);
+
+ await controller.invalidateSession();
+
+ assert.false(session.isAuthenticated);
+ });
+
+ test('disconnect action de-authenticates a user and resets cluster url', async function (assert) {
+ const url = 'http://localhost:9200';
+ await clusterUrl.setClusterUrl(url);
+
+ assert.true(session.isAuthenticated);
+ assert.strictEqual(clusterUrl.rendererClusterUrl, url);
+ assert.strictEqual(mockIPC.clusterUrl, url);
+
+ await controller.disconnect();
+
+ assert.false(session.isAuthenticated);
+ assert.notOk(clusterUrl.rendererClusterUrl);
+ assert.notOk(mockIPC.clusterUrl);
});
});
diff --git a/ui/desktop/tests/unit/controllers/cluster-url-test.js b/ui/desktop/tests/unit/controllers/cluster-url-test.js
new file mode 100644
index 000000000..7b889735c
--- /dev/null
+++ b/ui/desktop/tests/unit/controllers/cluster-url-test.js
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+import { authenticateSession } from 'ember-simple-auth/test-support';
+import { setupIntl } from 'ember-intl/test-support';
+import WindowMockIPC from '../../helpers/window-mock-ipc';
+
+module('Unit | Controller | cluster-url', function (hooks) {
+ setupTest(hooks);
+ setupIntl(hooks, 'en-us');
+
+ let controller;
+ let clusterUrl;
+ let mockIPC;
+
+ const setupMockIpc = (test) => {
+ test.owner.register('service:browser/window', WindowMockIPC);
+ mockIPC = test.owner.lookup('service:browser/window').mockIPC;
+ };
+
+ hooks.beforeEach(async function () {
+ authenticateSession({});
+ controller = this.owner.lookup('controller:cluster-url');
+ clusterUrl = this.owner.lookup('service:cluster-url');
+ setupMockIpc(this);
+ });
+
+ test('it exists', function (assert) {
+ assert.ok(controller);
+ });
+
+ test('setClusterUrl action de-authenticates a user and resets cluster url', async function (assert) {
+ const newClusterUrl = 'http://localhost:9200';
+ assert.notEqual(clusterUrl.rendererClusterUrl, newClusterUrl);
+ assert.notEqual(mockIPC.clusterUrl, newClusterUrl);
+
+ await controller.setClusterUrl(newClusterUrl);
+
+ assert.strictEqual(clusterUrl.rendererClusterUrl, newClusterUrl);
+ assert.strictEqual(mockIPC.clusterUrl, newClusterUrl);
+ });
+});
diff --git a/ui/desktop/tests/unit/controllers/error-test.js b/ui/desktop/tests/unit/controllers/error-test.js
new file mode 100644
index 000000000..595941787
--- /dev/null
+++ b/ui/desktop/tests/unit/controllers/error-test.js
@@ -0,0 +1,17 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+
+module('Unit | Controller | error', function (hooks) {
+ setupTest(hooks);
+
+ test('it exists', function (assert) {
+ let controller = this.owner.lookup('controller:error');
+ assert.ok(controller);
+ assert.ok(controller.appUtilities);
+ });
+});
diff --git a/ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/index-test.js b/ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/index-test.js
new file mode 100644
index 000000000..1303957be
--- /dev/null
+++ b/ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/index-test.js
@@ -0,0 +1,79 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import { module, test } from 'qunit';
+import { setupTest } from 'ember-qunit';
+import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
+import { authenticateSession } from 'ember-simple-auth/test-support';
+import { setupIntl } from 'ember-intl/test-support';
+import { TYPE_AUTH_METHOD_PASSWORD } from 'api/models/auth-method';
+
+module(
+ 'Unit | Controller | scopes/scope/authenticate/method/index',
+ function (hooks) {
+ setupTest(hooks);
+ setupMirage(hooks);
+ setupIntl(hooks, 'en-us');
+
+ let controller;
+ let store;
+ let session;
+
+ const instances = {
+ scopes: {
+ global: null,
+ },
+ authMethod: null,
+ };
+
+ hooks.beforeEach(async function () {
+ authenticateSession({});
+ controller = this.owner.lookup(
+ 'controller:scopes/scope/authenticate/method/index',
+ );
+ store = this.owner.lookup('service:store');
+ session = this.owner.lookup('service:session');
+
+ instances.scopes.global = this.server.create('scope', {
+ id: 'global',
+ type: 'global',
+ });
+ instances.authMethod = this.server.create('auth-method', {
+ scope: instances.scopes.global,
+ type: TYPE_AUTH_METHOD_PASSWORD,
+ });
+ });
+
+ test('it exists', function (assert) {
+ assert.ok(controller);
+ });
+
+ test('authenticate action saves login information', async function (assert) {
+ const identification = 'admin123';
+ const authMethod = await store.findRecord(
+ 'auth-method',
+ instances.authMethod.id,
+ );
+ const { authenticator: authBefore, username: usernameBefore } =
+ session.data.authenticated;
+
+ assert.notOk(usernameBefore);
+ assert.strictEqual(authBefore, 'authenticator:test');
+
+ await controller.authenticate(authMethod, {
+ identification,
+ password: 'password',
+ });
+ const { authenticator: authAfter, username: usernameAfter } =
+ session.data.authenticated;
+
+ assert.strictEqual(
+ authAfter,
+ `authenticator:${instances.authMethod.type}`,
+ );
+ assert.strictEqual(usernameAfter, identification);
+ });
+ },
+);
diff --git a/ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js b/ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js
index 1b63d123c..a615dc9a6 100644
--- a/ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js
+++ b/ui/desktop/tests/unit/controllers/scopes/scope/authenticate/method/oidc-test.js
@@ -11,7 +11,6 @@ module(
function (hooks) {
setupTest(hooks);
- // TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup(
'controller:scopes/scope/authenticate/method/oidc',
diff --git a/ui/desktop/tests/unit/controllers/scopes/scope/projects/error-test.js b/ui/desktop/tests/unit/controllers/scopes/scope/projects/error-test.js
new file mode 100644
index 000000000..5ac8ad873
--- /dev/null
+++ b/ui/desktop/tests/unit/controllers/scopes/scope/projects/error-test.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) HashiCorp, Inc.
+ * SPDX-License-Identifier: BUSL-1.1
+ */
+
+import { module, test } from 'qunit';
+import { setupTest } from 'desktop/tests/helpers';
+
+module('Unit | Controller | scopes/scope/projects/error', function (hooks) {
+ setupTest(hooks);
+
+ test('it exists', function (assert) {
+ let controller = this.owner.lookup(
+ 'controller:scopes/scope/projects/error',
+ );
+ assert.ok(controller);
+ assert.ok(controller.appUtilities);
+ });
+});