UI: address flaky auth-jwt and control group tests (#28297)

* address flaky auth-jwt test

* refactor control group success test
This commit is contained in:
claire bontempo
2024-09-05 12:37:13 -07:00
committed by GitHub
parent cbec86fba5
commit c4962925de
3 changed files with 97 additions and 93 deletions

View File

@@ -9,7 +9,7 @@ export const AUTH_FORM = {
tabs: (method: string) => (method ? `[data-test-auth-method="${method}"]` : '[data-test-auth-method]'), tabs: (method: string) => (method ? `[data-test-auth-method="${method}"]` : '[data-test-auth-method]'),
description: '[data-test-description]', description: '[data-test-description]',
roleInput: '[data-test-role]', roleInput: '[data-test-role]',
input: (item: string) => `[data-test-${item}]`, // i.e. role, token, password or username input: (item: string) => `[data-test-${item}]`, // i.e. jwt, role, token, password or username
mountPathInput: '[data-test-auth-form-mount-path]', mountPathInput: '[data-test-auth-form-mount-path]',
moreOptions: '[data-test-auth-form-options-toggle]', moreOptions: '[data-test-auth-form-options-toggle]',
}; };

View File

@@ -6,16 +6,17 @@
import { _cancelTimers as cancelTimers } from '@ember/runloop'; import { _cancelTimers as cancelTimers } from '@ember/runloop';
import { module, test } from 'qunit'; import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit'; import { setupRenderingTest } from 'ember-qunit';
import { render, settled, waitUntil } from '@ember/test-helpers'; import { fillIn, render, settled, waitUntil } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile'; import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon'; import sinon from 'sinon';
import { resolve } from 'rsvp'; import { resolve } from 'rsvp';
import { create } from 'ember-cli-page-object'; import { create } from 'ember-cli-page-object';
import form from '../../pages/components/auth-jwt'; import form from '../../pages/components/auth-jwt';
import { ERROR_WINDOW_CLOSED, ERROR_MISSING_PARAMS, ERROR_JWT_LOGIN } from 'vault/components/auth-jwt'; import { ERROR_WINDOW_CLOSED, ERROR_MISSING_PARAMS, ERROR_JWT_LOGIN } from 'vault/components/auth-jwt';
import { fakeWindow, buildMessage } from '../../helpers/oidc-window-stub'; import { fakeWindow, buildMessage } from 'vault/tests/helpers/oidc-window-stub';
import { setupMirage } from 'ember-cli-mirage/test-support'; import { setupMirage } from 'ember-cli-mirage/test-support';
import { overrideResponse } from 'vault/tests/helpers/stubs'; import { overrideResponse } from 'vault/tests/helpers/stubs';
import { AUTH_FORM } from 'vault/tests/helpers/auth/auth-form-selectors';
const component = create(form); const component = create(form);
const windows = []; const windows = [];
@@ -33,12 +34,6 @@ fakeWindow.reopen({
}, },
}); });
const OIDC_AUTH_RESPONSE = {
auth: {
client_token: 'token',
},
};
const renderIt = async (context, path = 'jwt') => { const renderIt = async (context, path = 'jwt') => {
const handler = (data, e) => { const handler = (data, e) => {
if (e && e.preventDefault) e.preventDefault(); if (e && e.preventDefault) e.preventDefault();
@@ -75,11 +70,11 @@ module('Integration | Component | auth jwt', function (hooks) {
}, },
}); });
this.server.get('/auth/:path/oidc/callback', function () { this.server.get('/auth/:path/oidc/callback', function () {
return OIDC_AUTH_RESPONSE; return { auth: { client_token: 'token' } };
}); });
this.server.post('/auth/:path/oidc/auth_url', (_, request) => { this.server.post('/auth/:path/oidc/auth_url', (_, request) => {
const { role } = JSON.parse(request.requestBody); const { role } = JSON.parse(request.requestBody);
if (['test', 'bar'].includes(role)) { if (['okta', 'test', 'bar'].includes(role)) {
const auth_url = role === 'test' ? 'http://example.com' : role === 'okta' ? 'http://okta.com' : ''; const auth_url = role === 'test' ? 'http://example.com' : role === 'okta' ? 'http://okta.com' : '';
return { return {
data: { auth_url }, data: { auth_url },
@@ -127,35 +122,46 @@ module('Integration | Component | auth jwt', function (hooks) {
}); });
test('oidc: test role: it renders', async function (assert) { test('oidc: test role: it renders', async function (assert) {
// setting the path also fires off a request to auth_url but this happens inconsistently in tests
// setting here so it doesn't affect the postCount because it's not relevant to what's being tested
this.set('selectedAuthPath', 'foo');
let postCount = 0; let postCount = 0;
this.server.post('/auth/:path/oidc/auth_url', (_, request) => { this.server.post('/auth/:path/oidc/auth_url', (_, request) => {
postCount++; postCount++;
const { role } = JSON.parse(request.requestBody); const { role } = JSON.parse(request.requestBody);
if (['test', 'okta', 'bar'].includes(role)) { const auth_url = role === 'test' ? 'http://example.com' : role === 'okta' ? 'http://okta.com' : '';
const auth_url = role === 'test' ? 'http://example.com' : role === 'okta' ? 'http://okta.com' : ''; return {
return { data: { auth_url },
data: { auth_url }, };
};
}
const errors = role === 'foo' ? ['role "foo" could not be found'] : [ERROR_JWT_LOGIN];
return overrideResponse(400, { errors });
}); });
await renderIt(this); await renderIt(this);
await settled(); await settled();
this.set('selectedAuthPath', 'foo'); await fillIn(AUTH_FORM.roleInput, 'test');
await component.role('test'); assert
await settled(); .dom(AUTH_FORM.input('jwt'))
assert.notOk(component.jwtPresent, 'does not show jwt input for OIDC type login'); .doesNotExist('does not show jwt token input if role matches OIDC login url');
assert.strictEqual(component.loginButtonText, 'Sign in with OIDC Provider'); assert.dom(AUTH_FORM.login).hasText('Sign in with OIDC Provider');
await fillIn(AUTH_FORM.roleInput, 'okta');
await component.role('okta');
// 1 for initial render, 1 for each time role changed = 3 // 1 for initial render, 1 for each time role changed = 3
assert.strictEqual(postCount, 3, 'fetches the auth_url when the path changes'); assert.strictEqual(postCount, 3, 'fetches the auth_url when the role changes');
assert.strictEqual( assert.dom(AUTH_FORM.login).hasText('Sign in with Okta', 'recognizes auth methods with certain urls');
component.loginButtonText, });
'Sign in with Okta',
'recognizes auth methods with certain urls' test('oidc: it fetches auth_url when path changes', async function (assert) {
); assert.expect(2);
this.set('selectedAuthPath', 'foo');
await renderIt(this);
// auth_url is requested on initial render so stubbing after rendering the component
// to test auth_url is called when the :path changes
this.server.post('/auth/:path/oidc/auth_url', (_, request) => {
assert.true(true, 'request is made to auth_url');
assert.strictEqual(request?.params?.path, 'foo', 'request params are { path: foo }');
return {
data: { auth_url: '' },
};
});
this.set('selectedAuthPath', 'foo');
await settled();
}); });
test('oidc: it calls window.open popup window on login', async function (assert) { test('oidc: it calls window.open popup window on login', async function (assert) {

View File

@@ -3,97 +3,95 @@
* SPDX-License-Identifier: BUSL-1.1 * SPDX-License-Identifier: BUSL-1.1
*/ */
import { later, run, _cancelTimers as cancelTimers } from '@ember/runloop';
import { resolve } from 'rsvp'; import { resolve } from 'rsvp';
import Service from '@ember/service'; import Service from '@ember/service';
import { module, test } from 'qunit'; import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit'; import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers'; import { click, fillIn, find, render } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile'; import hbs from 'htmlbars-inline-precompile';
import sinon from 'sinon'; import sinon from 'sinon';
import { create } from 'ember-cli-page-object';
import controlGroupSuccess from '../../pages/components/control-group-success';
import { setRunOptions } from 'ember-a11y-testing/test-support'; import { setRunOptions } from 'ember-a11y-testing/test-support';
const component = create(controlGroupSuccess); const SELECTORS = {
jsonViewer: '[data-test-json-viewer]',
const controlGroupService = Service.extend({ navigate: '[data-test-navigate-button]',
deleteControlGroupToken: sinon.stub(), navMessage: '[data-test-navigate-message]',
markTokenForUnwrap: sinon.stub(), tokenInput: '[data-test-token-input]',
}); unwrap: '[data-test-unwrap-button]',
unwrapForm: '[data-test-unwrap-form]',
const storeService = Service.extend({ };
adapterFor() {
return {
toolAction() {
return resolve({ data: { foo: 'bar' } });
},
};
},
});
module('Integration | Component | control group success', function (hooks) { module('Integration | Component | control group success', function (hooks) {
setupRenderingTest(hooks); setupRenderingTest(hooks);
hooks.beforeEach(function () { hooks.beforeEach(function () {
run(() => { this.store = this.owner.lookup('service:store');
this.owner.unregister('service:store'); this.transitionStub = sinon.stub(this.owner.lookup('service:router'), 'transitionTo');
this.owner.register('service:control-group', controlGroupService); this.controlGroup = this.owner.lookup('service:control-group');
this.controlGroup = this.owner.lookup('service:control-group'); this.markTokenForUnwrapStub = sinon.stub(this.controlGroup, 'markTokenForUnwrap');
this.owner.register('service:store', storeService); this.model = {
this.router = this.owner.lookup('service:router'); approved: false,
this.router.reopen({ requestPath: 'foo/bar',
transitionTo: sinon.stub().returns(resolve()), id: 'accessor',
}); requestEntity: { id: 'requestor', name: 'entity8509' },
setRunOptions({ reload: sinon.stub(),
rules: { };
// TODO: swap out JsonEditor with Hds::CodeBlock for display setRunOptions({
'color-contrast': { enabled: false }, rules: {
label: { enabled: false }, // TODO: swap out JsonEditor with Hds::CodeBlock for display
}, 'color-contrast': { enabled: false },
}); label: { enabled: false },
},
}); });
}); });
const MODEL = {
approved: false,
requestPath: 'foo/bar',
id: 'accessor',
requestEntity: { id: 'requestor', name: 'entity8509' },
reload: sinon.stub(),
};
test('render with saved token', async function (assert) { test('render with saved token', async function (assert) {
assert.expect(3); assert.expect(3);
const response = { const response = {
uiParams: { url: '/foo' }, uiParams: { url: '/foo' },
token: 'token', token: 'token',
}; };
this.set('model', MODEL);
this.set('response', response); this.set('response', response);
await render(hbs`<ControlGroupSuccess @model={{this.model}} @controlGroupResponse={{this.response}} />`); await render(hbs`<ControlGroupSuccess @model={{this.model}} @controlGroupResponse={{this.response}} />`);
assert
.dom(SELECTORS.navMessage)
.hasText(
'You have been granted access to foo/bar. Be careful, you can only access this data once. If you need access again in the future you will need to get authorized again. Visit'
);
assert.true(component.showsNavigateMessage, 'shows unwrap message'); await click(SELECTORS.navigate);
const [transition] = this.transitionStub.lastCall.args;
await component.navigate(); const [accessor] = this.markTokenForUnwrapStub.lastCall.args;
later(() => cancelTimers(), 50); assert.strictEqual(accessor, 'accessor', 'marks token for unwrap');
await settled(); assert.strictEqual(transition, '/foo', 'calls router transition');
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) { test('render without token', async function (assert) {
assert.expect(2); assert.expect(2);
this.set('model', MODEL);
await render(hbs`<ControlGroupSuccess @model={{this.model}} />`); await render(hbs`<ControlGroupSuccess @model={{this.model}} />`);
assert.dom(SELECTORS.unwrapForm).exists();
assert.dom(SELECTORS.tokenInput).hasValue('');
});
assert.true(component.showsUnwrapForm, 'shows unwrap form'); test('it unwraps data on submit', async function (assert) {
assert.expect(2);
await component.token('token'); const storeService = Service.extend({
component.unwrap(); adapterFor() {
later(() => cancelTimers(), 50); return {
await settled(); toolAction() {
return resolve({ data: { foo: 'bar' } });
assert.true(component.showsJsonViewer, 'shows unwrapped data'); },
};
},
});
this.owner.unregister('service:store');
this.owner.register('service:store', storeService);
await render(hbs`<ControlGroupSuccess @model={{this.model}} />`);
assert.dom(SELECTORS.tokenInput).hasValue('');
await fillIn(SELECTORS.tokenInput, 'token');
await click(SELECTORS.unwrap);
const actual = find(SELECTORS.jsonViewer).innerText;
const expected = JSON.stringify({ foo: 'bar' }, null, 2);
assert.strictEqual(actual, expected, `it renders unwrapped data: ${actual}`);
}); });
}); });