diff --git a/ui/app/components/auth-jwt.js b/ui/app/components/auth-jwt.js
index a373ee86cf..610b6d0993 100644
--- a/ui/app/components/auth-jwt.js
+++ b/ui/app/components/auth-jwt.js
@@ -2,14 +2,13 @@
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
-
+import Ember from 'ember';
import { service } from '@ember/service';
// ARG NOTE: Once you remove outer-html after glimmerizing you can remove the outer-html component
import Component from './outer-html';
import { task, timeout, waitForEvent } from 'ember-concurrency';
import { debounce } from '@ember/runloop';
-const WAIT_TIME = 500;
const ERROR_WINDOW_CLOSED =
'The provider window was closed before authentication was complete. Your web browser may have blocked or closed a pop-up window. Please check your settings and click Sign In to try again.';
const ERROR_MISSING_PARAMS =
@@ -109,6 +108,8 @@ export default Component.extend({
watchPopup: task(function* (oidcWindow) {
while (true) {
+ const WAIT_TIME = Ember.testing ? 50 : 500;
+
yield timeout(WAIT_TIME);
if (!oidcWindow || oidcWindow.closed) {
return this.handleOIDCError(ERROR_WINDOW_CLOSED);
diff --git a/ui/app/components/mfa/mfa-form.js b/ui/app/components/mfa/mfa-form.js
index 20b09ac4f6..484d13905f 100644
--- a/ui/app/components/mfa/mfa-form.js
+++ b/ui/app/components/mfa/mfa-form.js
@@ -3,6 +3,7 @@
* SPDX-License-Identifier: BUSL-1.1
*/
+import Ember from 'ember';
import Component from '@glimmer/component';
import { service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
@@ -29,7 +30,7 @@ export const TOTP_VALIDATION_ERROR =
export default class MfaForm extends Component {
@service auth;
- @tracked countdown;
+ @tracked countdown = 0;
@tracked error;
@tracked codeDelayMessage;
@@ -104,7 +105,10 @@ export default class MfaForm extends Component {
@task *newCodeDelay(message) {
// parse validity period from error string to initialize countdown
this.countdown = parseInt(message.match(/(\d\w seconds)/)[0].split(' ')[0]);
- while (this.countdown) {
+
+ if (Ember.testing) return;
+
+ while (this.countdown > 0) {
yield timeout(1000);
this.countdown--;
}
diff --git a/ui/app/templates/components/mfa/mfa-form.hbs b/ui/app/templates/components/mfa/mfa-form.hbs
index f5025b6457..c1f1b1d911 100644
--- a/ui/app/templates/components/mfa/mfa-form.hbs
+++ b/ui/app/templates/components/mfa/mfa-form.hbs
@@ -43,7 +43,7 @@
placeholder={{if (gt constraint.methods.length 1) "Enter passcode"}}
spellcheck="false"
autofocus="true"
- disabled={{or this.validate.isRunning this.newCodeDelay.isRunning}}
+ disabled={{or this.validate.isRunning this.countdown}}
@value={{constraint.passcode}}
data-test-mfa-passcode={{index}}
/>
@@ -56,7 +56,7 @@
{{/if}}
{{/each}}
- {{#if this.newCodeDelay.isRunning}}
+ {{#if this.countdown}}
@@ -66,10 +66,10 @@
@icon={{if this.validate.isRunning "loading"}}
id="validate"
type="submit"
- disabled={{or this.validate.isRunning this.newCodeDelay.isRunning}}
+ disabled={{or this.validate.isRunning this.countdown}}
data-test-mfa-validate
/>
- {{#if this.newCodeDelay.isRunning}}
+ {{#if this.countdown}}
{{this.countdown}}
{{/if}}
diff --git a/ui/tests/acceptance/oidc-auth-method-test.js b/ui/tests/acceptance/oidc-auth-method-test.js
index 82fb72a5e8..0dcaaffd2f 100644
--- a/ui/tests/acceptance/oidc-auth-method-test.js
+++ b/ui/tests/acceptance/oidc-auth-method-test.js
@@ -70,6 +70,7 @@ module('Acceptance | oidc auth method', function (hooks) {
window.postMessage(buildMessage().data, window.origin);
cancelTimers();
}, 100);
+
await click('[data-test-auth-submit]');
});
@@ -98,6 +99,7 @@ module('Acceptance | oidc auth method', function (hooks) {
window.postMessage(buildMessage().data, window.origin);
cancelTimers();
}, 50);
+
await click('[data-test-auth-submit]');
});
@@ -109,6 +111,7 @@ module('Acceptance | oidc auth method', function (hooks) {
window.postMessage(buildMessage().data, window.origin);
cancelTimers();
}, 50);
+
await click('[data-test-auth-submit]');
await waitUntil(() => find('[data-test-user-menu-trigger]'));
await click('[data-test-user-menu-trigger]');
diff --git a/ui/tests/integration/components/auth-form-test.js b/ui/tests/integration/components/auth-form-test.js
index 07060597b3..c82ce4acf1 100644
--- a/ui/tests/integration/components/auth-form-test.js
+++ b/ui/tests/integration/components/auth-form-test.js
@@ -147,7 +147,6 @@ module('Integration | Component | auth form', function (hooks) {
await this.renderComponent();
later(() => cancelTimers(), 50);
-
await settled();
assert.dom(GENERAL.messageError).hasText('Error Token unwrap failed: There was an error unwrapping!');
});
diff --git a/ui/tests/integration/components/auth-jwt-test.js b/ui/tests/integration/components/auth-jwt-test.js
index eb6c26cdb6..1512129ef3 100644
--- a/ui/tests/integration/components/auth-jwt-test.js
+++ b/ui/tests/integration/components/auth-jwt-test.js
@@ -166,7 +166,10 @@ module('Integration | Component | auth jwt', function (hooks) {
await waitUntil(() => {
return this.openSpy.calledOnce;
});
+
cancelTimers();
+ await settled();
+
const call = this.openSpy.getCall(0);
assert.deepEqual(
call.args,
@@ -201,6 +204,8 @@ module('Integration | Component | auth jwt', function (hooks) {
buildMessage({ data: { source: 'oidc-callback', state: 'state', foo: 'bar' } })
);
cancelTimers();
+ await settled();
+
assert.strictEqual(this.error, ERROR_MISSING_PARAMS, 'calls onError with params missing error');
});
@@ -226,9 +231,11 @@ module('Integration | Component | auth jwt', function (hooks) {
return this.openSpy.calledOnce;
});
this.window.trigger('message', buildMessage({ origin: 'http://hackerz.com' }));
+
cancelTimers();
await settled();
- assert.notOk(this.handler.called, 'should not call the submit handler');
+
+ assert.false(this.handler.called, 'should not call the submit handler');
});
test('oidc: fails silently when event is not trusted', async function (assert) {
@@ -242,7 +249,8 @@ module('Integration | Component | auth jwt', function (hooks) {
this.window.trigger('message', buildMessage({ isTrusted: false }));
cancelTimers();
await settled();
- assert.notOk(this.handler.called, 'should not call the submit handler');
+
+ assert.false(this.handler.called, 'should not call the submit handler');
});
test('oidc: it should trigger error callback when role is not found', async function (assert) {
diff --git a/ui/tests/integration/components/control-group-success-test.js b/ui/tests/integration/components/control-group-success-test.js
index bda51e70bf..64ccf0ccc9 100644
--- a/ui/tests/integration/components/control-group-success-test.js
+++ b/ui/tests/integration/components/control-group-success-test.js
@@ -71,25 +71,29 @@ module('Integration | Component | control group success', function (hooks) {
this.set('model', MODEL);
this.set('response', response);
await render(hbs``);
- assert.ok(component.showsNavigateMessage, 'shows unwrap message');
+
+ assert.true(component.showsNavigateMessage, 'shows unwrap message');
+
await component.navigate();
later(() => cancelTimers(), 50);
- return settled().then(() => {
- assert.ok(this.controlGroup.markTokenForUnwrap.calledOnce, 'marks token for unwrap');
- assert.ok(this.router.transitionTo.calledOnce, 'calls router transition');
- });
+ await settled();
+
+ assert.true(this.controlGroup.markTokenForUnwrap.calledOnce, 'marks token for unwrap');
+ assert.true(this.router.transitionTo.calledOnce, 'calls router transition');
});
test('render without token', async function (assert) {
assert.expect(2);
this.set('model', MODEL);
await render(hbs``);
- assert.ok(component.showsUnwrapForm, 'shows unwrap form');
+
+ assert.true(component.showsUnwrapForm, 'shows unwrap form');
+
await component.token('token');
component.unwrap();
later(() => cancelTimers(), 50);
- return settled().then(() => {
- assert.ok(component.showsJsonViewer, 'shows unwrapped data');
- });
+ await settled();
+
+ assert.true(component.showsJsonViewer, 'shows unwrapped data');
});
});
diff --git a/ui/tests/integration/components/edit-form-kmip-role-test.js b/ui/tests/integration/components/edit-form-kmip-role-test.js
index a949ca2e8c..dac56709a4 100644
--- a/ui/tests/integration/components/edit-form-kmip-role-test.js
+++ b/ui/tests/integration/components/edit-form-kmip-role-test.js
@@ -224,15 +224,15 @@ module('Integration | Component | edit form kmip role', function (hooks) {
click('[data-test-edit-form-submit]');
later(() => cancelTimers(), 50);
- return settled().then(() => {
- for (const afterStateKey of Object.keys(stateAfterSave)) {
- assert.strictEqual(
- model.get(afterStateKey),
- stateAfterSave[afterStateKey],
- `sets ${afterStateKey} on save`
- );
- }
- });
+ await settled();
+
+ for (const afterStateKey of Object.keys(stateAfterSave)) {
+ assert.strictEqual(
+ model.get(afterStateKey),
+ stateAfterSave[afterStateKey],
+ `sets ${afterStateKey} on save`
+ );
+ }
});
}
});
diff --git a/ui/tests/integration/components/edit-form-test.js b/ui/tests/integration/components/edit-form-test.js
index ee4f9ebf72..1c8a1a0596 100644
--- a/ui/tests/integration/components/edit-form-test.js
+++ b/ui/tests/integration/components/edit-form-test.js
@@ -68,12 +68,12 @@ module('Integration | Component | edit form', function (hooks) {
component.submit();
later(() => cancelTimers(), 50);
- return settled().then(() => {
- assert.ok(saveSpy.calledOnce, 'calls passed onSave');
- assert.strictEqual(saveSpy.getCall(0).args[0].saveType, 'save');
- assert.deepEqual(saveSpy.getCall(0).args[0].model, this.model, 'passes model to onSave');
- const flash = this.owner.lookup('service:flash-messages');
- assert.strictEqual(flash.success.callCount, 1, 'calls flash message success');
- });
+ await settled();
+
+ assert.true(saveSpy.calledOnce, 'calls passed onSave');
+ assert.strictEqual(saveSpy.getCall(0).args[0].saveType, 'save');
+ assert.deepEqual(saveSpy.getCall(0).args[0].model, this.model, 'passes model to onSave');
+ const flash = this.owner.lookup('service:flash-messages');
+ assert.strictEqual(flash.success.callCount, 1, 'calls flash message success');
});
});
diff --git a/ui/tests/integration/components/mfa-form-test.js b/ui/tests/integration/components/mfa-form-test.js
index c5c65ed696..128b5b3851 100644
--- a/ui/tests/integration/components/mfa-form-test.js
+++ b/ui/tests/integration/components/mfa-form-test.js
@@ -5,10 +5,9 @@
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
-import { render } from '@ember/test-helpers';
+import { render, settled, fillIn, click, waitUntil, waitFor } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { setupMirage } from 'ember-cli-mirage/test-support';
-import { fillIn, click, waitUntil } from '@ember/test-helpers';
import { _cancelTimers as cancelTimers, later } from '@ember/runloop';
import { TOTP_VALIDATION_ERROR } from 'vault/components/mfa/mfa-form';
@@ -84,6 +83,12 @@ module('Integration | Component | mfa-form', function (hooks) {
);
});
+ test('it should render a submit button', async function (assert) {
+ await render(hbs``);
+
+ assert.dom('[data-test-mfa-validate]').isNotDisabled('Button is not disabled by default');
+ });
+
test('it should render method selects and passcode inputs', async function (assert) {
assert.expect(2);
const duoConstraint = this.server.create('mfa-method', { type: 'duo', uses_passcode: true });
@@ -170,7 +175,6 @@ module('Integration | Component | mfa-form', function (hooks) {
await click('[data-test-mfa-validate]');
});
- // TODO JLR: It doesn't appear that cancelTimers is working and tests wait for the full countdown
test('it should show countdown on passcode already used and rate limit errors', async function (assert) {
const messages = {
used: 'code already used; new code is available in 45 seconds',
@@ -184,12 +188,16 @@ module('Integration | Component | mfa-form', function (hooks) {
throw { errors: [messages[code]] };
},
});
+ const expectedTime = code === 'used' ? 45 : 15;
+
await render(hbs``);
await fillIn('[data-test-mfa-passcode]', code);
- later(() => cancelTimers(), 50);
+
await click('[data-test-mfa-validate]');
- const expectedTime = code === 'used' ? '45' : '15';
+
+ await waitFor('[data-test-mfa-countdown]');
+
assert
.dom('[data-test-mfa-countdown]')
.includesText(expectedTime, 'countdown renders with correct initial value from error response');
@@ -209,6 +217,8 @@ module('Integration | Component | mfa-form', function (hooks) {
await fillIn('[data-test-mfa-passcode]', 'test-code');
later(() => cancelTimers(), 50);
+ await settled();
+
await click('[data-test-mfa-validate]');
assert
.dom('[data-test-message-error]')
diff --git a/ui/tests/integration/components/mount-backend-form-test.js b/ui/tests/integration/components/mount-backend-form-test.js
index f1acedc1fe..b824faa51b 100644
--- a/ui/tests/integration/components/mount-backend-form-test.js
+++ b/ui/tests/integration/components/mount-backend-form-test.js
@@ -116,8 +116,8 @@ module('Integration | Component | mount backend form', function (hooks) {
later(() => cancelTimers(), 50);
await settled();
- assert.ok(spy.calledOnce, 'calls the passed success method');
- assert.ok(
+ assert.true(spy.calledOnce, 'calls the passed success method');
+ assert.true(
this.flashSuccessSpy.calledWith('Successfully mounted the approle auth method at foo.'),
'Renders correct flash message'
);
@@ -184,8 +184,8 @@ module('Integration | Component | mount backend form', function (hooks) {
later(() => cancelTimers(), 50);
await settled();
- assert.ok(spy.calledOnce, 'calls the passed success method');
- assert.ok(
+ assert.true(spy.calledOnce, 'calls the passed success method');
+ assert.true(
this.flashSuccessSpy.calledWith('Successfully mounted the ssh secrets engine at foo.'),
'Renders correct flash message'
);
diff --git a/ui/tests/unit/components/auth-jwt-test.js b/ui/tests/unit/components/auth-jwt-test.js
index d4c3b0d066..41ba7e3d65 100644
--- a/ui/tests/unit/components/auth-jwt-test.js
+++ b/ui/tests/unit/components/auth-jwt-test.js
@@ -5,6 +5,7 @@
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
+import { settled } from '@ember/test-helpers';
import EmberObject from '@ember/object';
import Evented from '@ember/object/evented';
import sinon from 'sinon';
@@ -29,9 +30,14 @@ module('Unit | Component | auth-jwt', function (hooks) {
this.component.prepareForOIDC.perform(mockWindow.create());
this.component.window.trigger('message', { origin: 'http://anotherdomain.com', isTrusted: true });
- assert.ok(this.errorSpy.notCalled, 'Error handler not triggered while waiting for oidc callback message');
+ assert.true(
+ this.errorSpy.notCalled,
+ 'Error handler not triggered while waiting for oidc callback message'
+ );
assert.strictEqual(this.component.exchangeOIDC.performCount, 0, 'exchangeOIDC method not fired');
+
cancelTimers();
+ await settled();
});
test('it should ignore untrusted messages while waiting for oidc callback', async function (assert) {
@@ -40,10 +46,11 @@ module('Unit | Component | auth-jwt', function (hooks) {
this.component.window.trigger('message', { origin: 'http://localhost:4200', isTrusted: false });
assert.ok(this.errorSpy.notCalled, 'Error handler not triggered while waiting for oidc callback message');
assert.strictEqual(this.component.exchangeOIDC.performCount, 0, 'exchangeOIDC method not fired');
+
cancelTimers();
+ await settled();
});
- // TODO: Flaky
// test case for https://github.com/hashicorp/vault/issues/12436
test('it should ignore messages sent from outside the app while waiting for oidc callback', async function (assert) {
assert.expect(2);
@@ -65,12 +72,17 @@ module('Unit | Component | auth-jwt', function (hooks) {
message.data.source = 'oidc-callback';
this.component.window.trigger('message', message);
- assert.ok(this.errorSpy.notCalled, 'Error handler not triggered while waiting for oidc callback message');
+ assert.true(
+ this.errorSpy.notCalled,
+ 'Error handler not triggered while waiting for oidc callback message'
+ );
assert.strictEqual(
this.component.exchangeOIDC.performCount,
1,
'exchangeOIDC method fires when oidc callback message is received'
);
+
cancelTimers();
+ await settled();
});
});