Add password credential form (#3054)
* feat: 🎸 add password credential form * test: 💍 add a11y ignores * test: 💍 fix e2e tests * test: 💍 add password cred type to targets brokering tests * chore: 🤖 remove comment
This commit is contained in:
1 parent
bbb5808875
commit
6bb237e78c
14 files changed
+693
-30
No files matched your search
@@ -147,7 +147,7 @@ export class CredentialStoresPage extends BaseResourcePage {
|
||||
await this.page.getByRole('link', { name: 'New Credential' }).click();
|
||||
}
|
||||
|
||||
await this.page.getByLabel('Name (Optional)').fill(credentialName);
|
||||
await this.page.getByLabel('Name', { exact: true }).fill(credentialName);
|
||||
await this.page.getByLabel('Description').fill('This is an automated test');
|
||||
await this.page.getByRole('combobox', { name: 'Type' }).click();
|
||||
await this.page
|
||||
@@ -207,7 +207,7 @@ export class CredentialStoresPage extends BaseResourcePage {
|
||||
}
|
||||
|
||||
const credentialName = 'Credential ' + nanoid();
|
||||
await this.page.getByLabel('Name (Optional)').fill(credentialName);
|
||||
await this.page.getByLabel('Name', { exact: true }).fill(credentialName);
|
||||
await this.page.getByLabel('Description').fill('This is an automated test');
|
||||
await this.page.getByRole('combobox', { name: 'Type' }).click();
|
||||
await this.page
|
||||
@@ -271,7 +271,7 @@ export class CredentialStoresPage extends BaseResourcePage {
|
||||
}
|
||||
|
||||
const credentialName = 'Credential ' + nanoid();
|
||||
await this.page.getByLabel('Name (Optional)').fill(credentialName);
|
||||
await this.page.getByLabel('Name', { exact: true }).fill(credentialName);
|
||||
await this.page.getByLabel('Description').fill('This is an automated test');
|
||||
await this.page.getByRole('combobox', { name: 'Type' }).click();
|
||||
await this.page
|
||||
|
||||
@@ -191,7 +191,7 @@ test(
|
||||
.getByRole('link', { name: 'Credentials', exact: true })
|
||||
.click();
|
||||
await page.getByRole('link', { name: 'New', exact: true }).click();
|
||||
await page.getByLabel('Name (Optional)').fill(credentialName);
|
||||
await page.getByLabel('Name', { exact: true }).fill(credentialName);
|
||||
await page.getByLabel('Description').fill('This is an automated test');
|
||||
await page.getByRole('combobox', { name: 'Type' }).click();
|
||||
await page.getByRole('option', { name: 'JSON' }).click();
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
name='name'
|
||||
@value={{@model.name}}
|
||||
@isInvalid={{@model.errors.name}}
|
||||
@isOptional={{true}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'name')}}
|
||||
as |F|
|
||||
@@ -37,7 +36,6 @@
|
||||
name='description'
|
||||
@value={{@model.description}}
|
||||
@isInvalid={{@model.errors.description}}
|
||||
@isOptional={{true}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
|
||||
@@ -1,4 +1,108 @@
|
||||
{{!
|
||||
Copyright (c) HashiCorp, Inc.
|
||||
SPDX-License-Identifier: BUSL-1.1
|
||||
}}
|
||||
}}
|
||||
|
||||
<Rose::Form
|
||||
@onSubmit={{@submit}}
|
||||
@cancel={{@cancel}}
|
||||
@disabled={{@model.isSaving}}
|
||||
@showEditToggle={{if @model.isNew false true}}
|
||||
as |form|
|
||||
>
|
||||
|
||||
<Hds::Form::TextInput::Field
|
||||
name='name'
|
||||
@value={{@model.name}}
|
||||
@isInvalid={{@model.errors.name}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'name')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.name.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.name.help'}}</F.HelperText>
|
||||
{{#if @model.errors.name}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.name as |error|}}
|
||||
<E.Message data-test-error-message-name>
|
||||
{{error.message}}
|
||||
</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
|
||||
<Hds::Form::Textarea::Field
|
||||
name='description'
|
||||
@value={{@model.description}}
|
||||
@isInvalid={{@model.errors.description}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.description.label'}}</F.Label>
|
||||
<F.HelperText>{{t 'form.description.help'}}</F.HelperText>
|
||||
{{#if @model.errors.description}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.description as |error|}}
|
||||
<E.Message>{{error.message}}</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::Textarea::Field>
|
||||
|
||||
{{#if @model.isNew}}
|
||||
<Form::Credential::Select
|
||||
@model={{@model}}
|
||||
@types={{@types}}
|
||||
@changeType={{@changeType}}
|
||||
/>
|
||||
{{else}}
|
||||
<InfoField
|
||||
@value={{t (concat 'resources.credential.types.' @model.type)}}
|
||||
name='type'
|
||||
readonly={{true}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'form.type.label'}}</F.Label>
|
||||
<F.HelperText>{{t (concat 'resources.credential.help.' @model.type)}}
|
||||
</F.HelperText>
|
||||
</InfoField>
|
||||
{{/if}}
|
||||
|
||||
{{#if (or @model.isNew form.isEditable)}}
|
||||
<Hds::Form::TextInput::Field
|
||||
name='password'
|
||||
@type='password'
|
||||
@value={{@model.password}}
|
||||
@isInvalid={{@model.errors.password}}
|
||||
@isRequired={{true}}
|
||||
disabled={{form.disabled}}
|
||||
autocomplete='new-password'
|
||||
{{on 'input' (set-from-event @model 'password')}}
|
||||
as |F|
|
||||
>
|
||||
<F.Label>{{t 'resources.credential.form.password.label'}}</F.Label>
|
||||
<F.HelperText>{{t
|
||||
'resources.credential.form.password.help'
|
||||
}}</F.HelperText>
|
||||
{{#if @model.errors.password}}
|
||||
<F.Error as |E|>
|
||||
{{#each @model.errors.password as |error|}}
|
||||
<E.Message data-test-error-message-password>
|
||||
{{error.message}}
|
||||
</E.Message>
|
||||
{{/each}}
|
||||
</F.Error>
|
||||
{{/if}}
|
||||
</Hds::Form::TextInput::Field>
|
||||
{{/if}}
|
||||
|
||||
{{#if (can 'save model' @model)}}
|
||||
<form.actions
|
||||
@enableEditText={{t 'actions.edit-form'}}
|
||||
@submitText={{t 'actions.save'}}
|
||||
@cancelText={{t 'actions.cancel'}}
|
||||
/>
|
||||
{{/if}}
|
||||
|
||||
</Rose::Form>
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
<Hds::Form::SuperSelect::Single::Field
|
||||
name={{t 'form.type.label'}}
|
||||
@isRequired={{true}}
|
||||
@onChange={{@changeType}}
|
||||
@selected={{@model.type}}
|
||||
@options={{@types}}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
name='name'
|
||||
@value={{@model.name}}
|
||||
@isInvalid={{@model.errors.name}}
|
||||
@isOptional={{true}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'name')}}
|
||||
as |F|
|
||||
@@ -37,7 +36,6 @@
|
||||
name='description'
|
||||
@value={{@model.description}}
|
||||
@isInvalid={{@model.errors.description}}
|
||||
@isOptional={{true}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
name='name'
|
||||
@value={{@model.name}}
|
||||
@isInvalid={{@model.errors.name}}
|
||||
@isOptional={{true}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'name')}}
|
||||
as |F|
|
||||
@@ -37,7 +36,6 @@
|
||||
name='description'
|
||||
@value={{@model.description}}
|
||||
@isInvalid={{@model.errors.description}}
|
||||
@isOptional={{true}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
name='name'
|
||||
@value={{@model.name}}
|
||||
@isInvalid={{@model.errors.name}}
|
||||
@isOptional={{true}}
|
||||
disabled={{form.disabled}}
|
||||
{{on 'input' (set-from-event @model 'name')}}
|
||||
as |F|
|
||||
@@ -37,7 +36,6 @@
|
||||
name='description'
|
||||
@value={{@model.description}}
|
||||
@isInvalid={{@model.errors.description}}
|
||||
@isOptional={{true}}
|
||||
disabled={{form.disabled}}
|
||||
as |F|
|
||||
>
|
||||
|
||||
@@ -11,6 +11,13 @@ import { Response } from 'miragejs';
|
||||
import * as selectors from './selectors';
|
||||
import * as commonSelectors from 'admin/tests/helpers/selectors';
|
||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
import {
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
|
||||
TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD,
|
||||
TYPE_CREDENTIAL_JSON,
|
||||
TYPE_CREDENTIAL_PASSWORD,
|
||||
} from 'api/models/credential';
|
||||
|
||||
module(
|
||||
'Acceptance | credential-stores | credentials | create',
|
||||
@@ -23,6 +30,7 @@ module(
|
||||
let getUsernameKeyPairCredentialCount;
|
||||
let getUsernamePasswordDomainCredentialCount;
|
||||
let getJsonCredentialCount;
|
||||
let getPasswordCredentialCount;
|
||||
let featuresService;
|
||||
|
||||
const instances = {
|
||||
@@ -68,19 +76,27 @@ module(
|
||||
};
|
||||
getUsernamePasswordCredentialCount = () => {
|
||||
return this.server.schema.credentials.where({
|
||||
type: 'username_password',
|
||||
type: TYPE_CREDENTIAL_USERNAME_PASSWORD,
|
||||
}).length;
|
||||
};
|
||||
getUsernameKeyPairCredentialCount = () => {
|
||||
return this.server.schema.credentials.where({ type: 'ssh_private_key' })
|
||||
.length;
|
||||
return this.server.schema.credentials.where({
|
||||
type: TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
|
||||
}).length;
|
||||
};
|
||||
getJsonCredentialCount = () => {
|
||||
return this.server.schema.credentials.where({ type: 'json' }).length;
|
||||
return this.server.schema.credentials.where({
|
||||
type: TYPE_CREDENTIAL_JSON,
|
||||
}).length;
|
||||
};
|
||||
getUsernamePasswordDomainCredentialCount = () => {
|
||||
return this.server.schema.credentials.where({
|
||||
type: 'username_password_domain',
|
||||
type: TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
|
||||
}).length;
|
||||
};
|
||||
getPasswordCredentialCount = () => {
|
||||
return this.server.schema.credentials.where({
|
||||
type: TYPE_CREDENTIAL_PASSWORD,
|
||||
}).length;
|
||||
};
|
||||
});
|
||||
@@ -253,6 +269,36 @@ module(
|
||||
assert.strictEqual(getJsonCredentialCount(), jsonCredentialCount + 1);
|
||||
});
|
||||
|
||||
test('users can create a new password credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const credentialsCount = getCredentialsCount();
|
||||
const passwordCredentialCount = getPasswordCredentialCount();
|
||||
await visit(urls.credentials);
|
||||
|
||||
await click(commonSelectors.HREF(urls.newCredential));
|
||||
await fillIn(
|
||||
commonSelectors.FIELD_NAME,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
await click(selectors.TYPE_SELECT);
|
||||
await click(selectors.FIELD_TYPE_PASSWORD);
|
||||
await click(commonSelectors.SAVE_BTN);
|
||||
|
||||
assert.strictEqual(getCredentialsCount(), credentialsCount + 1);
|
||||
assert.strictEqual(
|
||||
getPasswordCredentialCount(),
|
||||
passwordCredentialCount + 1,
|
||||
);
|
||||
});
|
||||
|
||||
test('users can cancel create new username & password credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -391,6 +437,32 @@ module(
|
||||
assert.dom(selectors.EDITOR).includesText('{}');
|
||||
});
|
||||
|
||||
test('users can cancel creation of new password credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const credentialsCount = getCredentialsCount();
|
||||
await visit(urls.credentials);
|
||||
|
||||
await click(commonSelectors.HREF(urls.newCredential));
|
||||
await fillIn(
|
||||
commonSelectors.FIELD_NAME,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
await click(selectors.TYPE_SELECT);
|
||||
await click(selectors.FIELD_TYPE_PASSWORD);
|
||||
await click(commonSelectors.CANCEL_BTN);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.credentials);
|
||||
assert.strictEqual(getCredentialsCount(), credentialsCount);
|
||||
});
|
||||
|
||||
test('users cannot navigate to new credential route without proper authorization', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -598,6 +670,49 @@ module(
|
||||
assert.dom(commonSelectors.ALERT_TOAST_BODY).hasText(errorMessage);
|
||||
});
|
||||
|
||||
test('saving a new password credential with invalid fields displays error messages', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const errorMessage = 'Error in provided request.';
|
||||
const errorDescription =
|
||||
'Field required for creating a password credential.';
|
||||
await visit(urls.credentials);
|
||||
this.server.post('/credentials', () => {
|
||||
return new Response(
|
||||
400,
|
||||
{},
|
||||
{
|
||||
status: 400,
|
||||
code: 'invalid_argument',
|
||||
message: errorMessage,
|
||||
details: {
|
||||
request_fields: [
|
||||
{
|
||||
name: 'attributes.password',
|
||||
description: errorDescription,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
await click(commonSelectors.HREF(urls.newCredential));
|
||||
await click(selectors.TYPE_SELECT);
|
||||
await click(selectors.FIELD_TYPE_PASSWORD);
|
||||
await click(commonSelectors.SAVE_BTN);
|
||||
|
||||
assert.dom(commonSelectors.ALERT_TOAST_BODY).hasText(errorMessage);
|
||||
assert.dom(selectors.FIELD_PASSWORD_ERROR).hasText(errorDescription);
|
||||
});
|
||||
|
||||
test('users cannot create a new credential without proper authorization', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
|
||||
@@ -15,6 +15,8 @@ import {
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
|
||||
TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD,
|
||||
TYPE_CREDENTIAL_JSON,
|
||||
TYPE_CREDENTIAL_PASSWORD,
|
||||
} from 'api/models/credential';
|
||||
|
||||
module(
|
||||
@@ -27,6 +29,7 @@ module(
|
||||
let getUsernameKeyPairCredentialCount;
|
||||
let getJSONCredentialCount;
|
||||
let getUsernamePasswordDomainCredentialCount;
|
||||
let getPasswordCredentialCount;
|
||||
|
||||
const mockResponseMessage = 'Oops.';
|
||||
const mockResponse = () => {
|
||||
@@ -57,6 +60,7 @@ module(
|
||||
usernameKeyPairCredential: null,
|
||||
jsonCredential: null,
|
||||
usernamePasswordDomainCredential: null,
|
||||
passwordCredential: null,
|
||||
};
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
@@ -94,7 +98,12 @@ module(
|
||||
instances.jsonCredential = this.server.create('credential', {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: 'json',
|
||||
type: TYPE_CREDENTIAL_JSON,
|
||||
});
|
||||
instances.passwordCredential = this.server.create('credential', {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: TYPE_CREDENTIAL_PASSWORD,
|
||||
});
|
||||
// Generate route URLs for resources
|
||||
urls.projectScope = `/scopes/${instances.scopes.project.id}`;
|
||||
@@ -105,6 +114,7 @@ module(
|
||||
urls.usernameKeyPairCredential = `${urls.credentials}/${instances.usernameKeyPairCredential.id}`;
|
||||
urls.usernamePasswordDomainCredential = `${urls.credentials}/${instances.usernamePasswordDomainCredential.id}`;
|
||||
urls.jsonCredential = `${urls.credentials}/${instances.jsonCredential.id}`;
|
||||
urls.passwordCredential = `${urls.credentials}/${instances.passwordCredential.id}`;
|
||||
// Generate resource counter
|
||||
getUsernamePasswordCredentialCount = () => {
|
||||
return this.server.schema.credentials.where({
|
||||
@@ -124,6 +134,11 @@ module(
|
||||
type: TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
|
||||
}).length;
|
||||
};
|
||||
getPasswordCredentialCount = () => {
|
||||
return this.server.schema.credentials.where({
|
||||
type: TYPE_CREDENTIAL_PASSWORD,
|
||||
}).length;
|
||||
};
|
||||
});
|
||||
|
||||
test('can delete username & password credential', async function (assert) {
|
||||
@@ -219,6 +234,29 @@ module(
|
||||
);
|
||||
});
|
||||
|
||||
test('can delete password credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const passwordCredentialCount = getPasswordCredentialCount();
|
||||
await visit(urls.passwordCredential);
|
||||
|
||||
await click(selectors.MANAGE_DROPDOWN);
|
||||
await click(selectors.MANAGE_DROPDOWN_DELETE);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.credentials);
|
||||
assert.strictEqual(
|
||||
getPasswordCredentialCount(),
|
||||
passwordCredentialCount - 1,
|
||||
);
|
||||
});
|
||||
|
||||
test('cannot delete a username & password credential without proper authorization', async function (assert) {
|
||||
const usernamePasswordCredentialCount =
|
||||
getUsernamePasswordCredentialCount();
|
||||
@@ -283,6 +321,30 @@ module(
|
||||
);
|
||||
});
|
||||
|
||||
test('cannot delete a password credential without proper authorization', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const passwordCredentialCount = getPasswordCredentialCount();
|
||||
instances.passwordCredential.authorized_actions =
|
||||
instances.passwordCredential.authorized_actions.filter(
|
||||
(item) => item !== 'delete',
|
||||
);
|
||||
await visit(urls.credentials);
|
||||
|
||||
await click(commonSelectors.HREF(urls.passwordCredential));
|
||||
|
||||
assert.strictEqual(currentURL(), urls.passwordCredential);
|
||||
assert.dom(selectors.MANAGE_DROPDOWN).doesNotExist();
|
||||
assert.strictEqual(getPasswordCredentialCount(), passwordCredentialCount);
|
||||
});
|
||||
|
||||
test('can accept delete username & password credential via dialog', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -359,6 +421,33 @@ module(
|
||||
assert.strictEqual(getJSONCredentialCount(), jsonCredentialCount - 1);
|
||||
});
|
||||
|
||||
test('can accept delete password credential via dialog', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
const passwordCredentialCount = getPasswordCredentialCount();
|
||||
await visit(urls.credentials);
|
||||
|
||||
await click(commonSelectors.HREF(urls.passwordCredential));
|
||||
await click(selectors.MANAGE_DROPDOWN);
|
||||
await click(selectors.MANAGE_DROPDOWN_DELETE);
|
||||
await click(commonSelectors.MODAL_WARNING_CONFIRM_BTN);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.credentials);
|
||||
assert.strictEqual(
|
||||
getPasswordCredentialCount(),
|
||||
passwordCredentialCount - 1,
|
||||
);
|
||||
});
|
||||
|
||||
test('can cancel delete username & password credential via dialog', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -463,6 +552,30 @@ module(
|
||||
assert.strictEqual(getJSONCredentialCount(), jsonCredentialCount);
|
||||
});
|
||||
|
||||
test('can cancel delete password credential via dialog', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
const passwordCredentialCount = getPasswordCredentialCount();
|
||||
await visit(urls.credentials);
|
||||
|
||||
await click(commonSelectors.HREF(urls.passwordCredential));
|
||||
await click(selectors.MANAGE_DROPDOWN);
|
||||
await click(selectors.MANAGE_DROPDOWN_DELETE);
|
||||
await click(commonSelectors.MODAL_WARNING_CANCEL_BTN);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.passwordCredential);
|
||||
assert.strictEqual(getPasswordCredentialCount(), passwordCredentialCount);
|
||||
});
|
||||
|
||||
test('deleting a username & password credential which errors displays error message', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -548,5 +661,24 @@ module(
|
||||
|
||||
assert.dom(commonSelectors.ALERT_TOAST_BODY).hasText('Oops.');
|
||||
});
|
||||
|
||||
test('deleting a password credential which errors displays error message', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.server.del('/credentials/:id', mockResponse);
|
||||
await visit(urls.passwordCredential);
|
||||
|
||||
await click(selectors.MANAGE_DROPDOWN);
|
||||
await click(selectors.MANAGE_DROPDOWN_DELETE);
|
||||
|
||||
assert.dom(commonSelectors.ALERT_TOAST_BODY).hasText(mockResponseMessage);
|
||||
});
|
||||
},
|
||||
);
|
||||
@@ -9,6 +9,13 @@ import { setupApplicationTest } from 'admin/tests/helpers';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import * as commonSelectors from 'admin/tests/helpers/selectors';
|
||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
import {
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
|
||||
TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD,
|
||||
TYPE_CREDENTIAL_JSON,
|
||||
TYPE_CREDENTIAL_PASSWORD,
|
||||
} from 'api/models/credential';
|
||||
|
||||
module('Acceptance | credential-stores | credentials | read', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
@@ -24,6 +31,7 @@ module('Acceptance | credential-stores | credentials | read', function (hooks) {
|
||||
usernameKeyPairCredential: null,
|
||||
jsonCredential: null,
|
||||
usernamePasswordDomainCredential: null,
|
||||
passwordCredential: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
@@ -36,6 +44,7 @@ module('Acceptance | credential-stores | credentials | read', function (hooks) {
|
||||
jsonCredential: null,
|
||||
unknownCredential: null,
|
||||
usernamePasswordDomainCredential: null,
|
||||
passwordCredential: null,
|
||||
};
|
||||
|
||||
hooks.beforeEach(async function () {
|
||||
@@ -55,26 +64,31 @@ module('Acceptance | credential-stores | credentials | read', function (hooks) {
|
||||
instances.usernamePasswordCredential = this.server.create('credential', {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: 'username_password',
|
||||
type: TYPE_CREDENTIAL_USERNAME_PASSWORD,
|
||||
});
|
||||
instances.usernameKeyPairCredential = this.server.create('credential', {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: 'ssh_private_key',
|
||||
type: TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
|
||||
});
|
||||
instances.jsonCredential = this.server.create('credential', {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: 'json',
|
||||
type: TYPE_CREDENTIAL_JSON,
|
||||
});
|
||||
instances.usernamePasswordDomainCredential = this.server.create(
|
||||
'credential',
|
||||
{
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: 'username_password_domain',
|
||||
type: TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
|
||||
},
|
||||
);
|
||||
instances.passwordCredential = this.server.create('credential', {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: TYPE_CREDENTIAL_PASSWORD,
|
||||
});
|
||||
|
||||
// Generate route URLs for resources
|
||||
urls.projectScope = `/scopes/${instances.scopes.project.id}`;
|
||||
@@ -85,6 +99,7 @@ module('Acceptance | credential-stores | credentials | read', function (hooks) {
|
||||
urls.usernameKeyPairCredential = `${urls.credentials}/${instances.usernameKeyPairCredential.id}`;
|
||||
urls.jsonCredential = `${urls.credentials}/${instances.jsonCredential.id}`;
|
||||
urls.usernamePasswordDomainCredential = `${urls.credentials}/${instances.usernamePasswordDomainCredential.id}`;
|
||||
urls.passwordCredential = `${urls.credentials}/${instances.passwordCredential.id}`;
|
||||
urls.unknownCredential = `${urls.credentials}/foo`;
|
||||
});
|
||||
|
||||
@@ -169,6 +184,26 @@ module('Acceptance | credential-stores | credentials | read', function (hooks) {
|
||||
assert.strictEqual(currentURL(), urls.usernamePasswordDomainCredential);
|
||||
});
|
||||
|
||||
test('visiting password credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await visit(urls.staticCredentialStore);
|
||||
await click(commonSelectors.HREF(urls.credentials));
|
||||
|
||||
assert.strictEqual(currentURL(), urls.credentials);
|
||||
|
||||
await click(commonSelectors.HREF(urls.passwordCredential));
|
||||
|
||||
assert.strictEqual(currentURL(), urls.passwordCredential);
|
||||
});
|
||||
|
||||
test('cannot navigate to a username & password credential form without proper authorization', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -266,6 +301,30 @@ module('Acceptance | credential-stores | credentials | read', function (hooks) {
|
||||
.doesNotExist();
|
||||
});
|
||||
|
||||
test('cannot navigate to a password credential form without proper authorization', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
instances.passwordCredential.authorized_actions =
|
||||
instances.passwordCredential.authorized_actions.filter(
|
||||
(item) => item != 'read',
|
||||
);
|
||||
await visit(urls.credentials);
|
||||
|
||||
assert
|
||||
.dom(commonSelectors.TABLE_RESOURCE_LINK(urls.usernamePasswordCredential))
|
||||
.isVisible();
|
||||
assert
|
||||
.dom(commonSelectors.TABLE_RESOURCE_LINK(urls.passwordCredential))
|
||||
.doesNotExist();
|
||||
});
|
||||
|
||||
test('visiting an unknown credential displays 404 message', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
export const FIELD_TYPE_SSH = '[data-option-index="1"]';
|
||||
export const FIELD_TYPE_USERNAME_PASSWORD = '[data-option-index="0"]';
|
||||
export const FIELD_TYPE_USERNAME_PASSWORD_DOMAIN = '[data-option-index="2"]';
|
||||
export const FIELD_TYPE_JSON = '[data-option-index="3"]';
|
||||
export const FIELD_TYPE_PASSWORD = '[data-option-index="4"]';
|
||||
export const FIELD_DOMAIN = '[name=domain]';
|
||||
export const FIELD_PASSWORD = '[name=password]';
|
||||
export const FIELD_USERNAME = '[name=username]';
|
||||
export const FIELD_TYPE_JSON = '[data-option-index="3"]';
|
||||
export const FIELD_DOMAIN_VALUE = 'g.com';
|
||||
export const FIELD_USERNAME_VALUE = 'username123';
|
||||
export const FIELD_PASSWORD_VALUE = 'password123';
|
||||
|
||||
@@ -18,6 +18,13 @@ import { Response } from 'miragejs';
|
||||
import * as selectors from './selectors';
|
||||
import * as commonSelectors from 'admin/tests/helpers/selectors';
|
||||
import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
import {
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
|
||||
TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD,
|
||||
TYPE_CREDENTIAL_JSON,
|
||||
TYPE_CREDENTIAL_PASSWORD,
|
||||
} from 'api/models/credential';
|
||||
|
||||
module(
|
||||
'Acceptance | credential-stores | credentials | update',
|
||||
@@ -35,6 +42,7 @@ module(
|
||||
usernameKeyPairCredential: null,
|
||||
jsonCredential: null,
|
||||
usernamePasswordDomainCredential: null,
|
||||
passwordCredential: null,
|
||||
};
|
||||
|
||||
const urls = {
|
||||
@@ -46,6 +54,7 @@ module(
|
||||
usernameKeyPairCredential: null,
|
||||
jsonCredential: null,
|
||||
usernamePasswordDomainCredential: null,
|
||||
passwordCredential: null,
|
||||
};
|
||||
|
||||
const mockResponseMessage = 'Error in provided request.';
|
||||
@@ -87,26 +96,31 @@ module(
|
||||
instances.usernamePasswordCredential = this.server.create('credential', {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: 'username_password',
|
||||
type: TYPE_CREDENTIAL_USERNAME_PASSWORD,
|
||||
});
|
||||
instances.usernameKeyPairCredential = this.server.create('credential', {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: 'ssh_private_key',
|
||||
type: TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
|
||||
});
|
||||
instances.jsonCredential = this.server.create('credential', {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: 'json',
|
||||
type: TYPE_CREDENTIAL_JSON,
|
||||
});
|
||||
instances.usernamePasswordDomainCredential = this.server.create(
|
||||
'credential',
|
||||
{
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: 'username_password_domain',
|
||||
type: TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
|
||||
},
|
||||
);
|
||||
instances.passwordCredential = this.server.create('credential', {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
type: TYPE_CREDENTIAL_PASSWORD,
|
||||
});
|
||||
|
||||
// Generate route URLs for resources
|
||||
urls.projectScope = `/scopes/${instances.scopes.project.id}`;
|
||||
@@ -117,6 +131,7 @@ module(
|
||||
urls.usernameKeyPairCredential = `${urls.credentials}/${instances.usernameKeyPairCredential.id}`;
|
||||
urls.jsonCredential = `${urls.credentials}/${instances.jsonCredential.id}`;
|
||||
urls.usernamePasswordDomainCredential = `${urls.credentials}/${instances.usernamePasswordDomainCredential.id}`;
|
||||
urls.passwordCredential = `${urls.credentials}/${instances.passwordCredential.id}`;
|
||||
});
|
||||
|
||||
test('can save changes to existing username & password credential', async function (assert) {
|
||||
@@ -231,6 +246,37 @@ module(
|
||||
assert.strictEqual(credential.attributes.domain, 'g.com');
|
||||
});
|
||||
|
||||
test('can save changes to existing password credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.notEqual(
|
||||
instances.passwordCredential.name,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
await visit(urls.passwordCredential);
|
||||
|
||||
await click(commonSelectors.EDIT_BTN);
|
||||
await fillIn(
|
||||
commonSelectors.FIELD_NAME,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
await click(commonSelectors.SAVE_BTN);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.passwordCredential);
|
||||
assert.strictEqual(
|
||||
this.server.schema.credentials.where({ type: TYPE_CREDENTIAL_PASSWORD })
|
||||
.models[0].name,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
});
|
||||
|
||||
test('cannot make changes to an existing username & password credential without proper authorization', async function (assert) {
|
||||
instances.usernamePasswordCredential.authorized_actions =
|
||||
instances.usernamePasswordCredential.authorized_actions.filter(
|
||||
@@ -262,6 +308,27 @@ module(
|
||||
assert.dom(commonSelectors.EDIT_BTN).doesNotExist();
|
||||
});
|
||||
|
||||
test('cannot make changes to an existing password credential without proper authorization', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
instances.passwordCredential.authorized_actions =
|
||||
instances.passwordCredential.authorized_actions.filter(
|
||||
(item) => item !== 'update',
|
||||
);
|
||||
await visit(urls.credentials);
|
||||
|
||||
await click(commonSelectors.HREF(urls.passwordCredential));
|
||||
|
||||
assert.dom(commonSelectors.EDIT_BTN).doesNotExist();
|
||||
});
|
||||
|
||||
test('can cancel changes to existing username & password credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -336,6 +403,33 @@ module(
|
||||
.hasValue(instances.jsonCredential.name);
|
||||
});
|
||||
|
||||
test('can cancel changes to existing password credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await visit(urls.passwordCredential);
|
||||
await click(commonSelectors.EDIT_BTN, 'Activate edit mode');
|
||||
await fillIn(
|
||||
commonSelectors.FIELD_NAME,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
await click(commonSelectors.CANCEL_BTN);
|
||||
|
||||
assert.notEqual(
|
||||
instances.passwordCredential.name,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
assert
|
||||
.dom(commonSelectors.FIELD_NAME)
|
||||
.hasValue(instances.passwordCredential.name);
|
||||
});
|
||||
|
||||
test('saving an existing username & password credential with invalid fields displays error message', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -405,6 +499,32 @@ module(
|
||||
.hasText(mockResponseDescription);
|
||||
});
|
||||
|
||||
test('saving an existing password credential with invalid fields displays error message', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
this.server.patch('/credentials/:id', mockResponse);
|
||||
await visit(urls.passwordCredential);
|
||||
|
||||
await click(commonSelectors.EDIT_BTN, 'Activate edit mode');
|
||||
await fillIn(
|
||||
commonSelectors.FIELD_NAME,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
await click(commonSelectors.SAVE_BTN);
|
||||
|
||||
assert.dom(commonSelectors.ALERT_TOAST_BODY).hasText(mockResponseMessage);
|
||||
assert
|
||||
.dom(commonSelectors.FIELD_NAME_ERROR)
|
||||
.hasText(mockResponseDescription);
|
||||
});
|
||||
|
||||
test('can discard unsaved username & password credential changes via dialog', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -530,6 +650,48 @@ module(
|
||||
}
|
||||
});
|
||||
|
||||
test('can discard unsaved password credential changes via dialog', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.expect(5);
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
assert.notEqual(
|
||||
instances.passwordCredential.name,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
await visit(urls.passwordCredential);
|
||||
|
||||
await click(commonSelectors.EDIT_BTN, 'Activate edit mode');
|
||||
await fillIn(
|
||||
commonSelectors.FIELD_NAME,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
assert.strictEqual(currentURL(), urls.passwordCredential);
|
||||
try {
|
||||
await visit(urls.credentials);
|
||||
} catch (e) {
|
||||
assert.dom(commonSelectors.MODAL_WARNING).isVisible();
|
||||
|
||||
await click(commonSelectors.MODAL_WARNING_CONFIRM_BTN, 'Click Discard');
|
||||
|
||||
assert.strictEqual(currentURL(), urls.credentials);
|
||||
assert.notEqual(
|
||||
this.server.schema.credentials.where({
|
||||
type: TYPE_CREDENTIAL_PASSWORD,
|
||||
}).models[0].name,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('can cancel discard unsaved username & password credential changes via dialog', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -679,6 +841,54 @@ module(
|
||||
}
|
||||
});
|
||||
|
||||
test('can cancel discard unsaved password credential changes via dialog', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
assert.expect(6);
|
||||
const confirmService = this.owner.lookup('service:confirm');
|
||||
confirmService.enabled = true;
|
||||
assert.notEqual(
|
||||
instances.passwordCredential.name,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
await visit(urls.passwordCredential);
|
||||
|
||||
await click(commonSelectors.EDIT_BTN);
|
||||
const credentialName = find(commonSelectors.FIELD_NAME).value;
|
||||
await fillIn(
|
||||
commonSelectors.FIELD_NAME,
|
||||
commonSelectors.FIELD_NAME_VALUE,
|
||||
);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.passwordCredential);
|
||||
|
||||
try {
|
||||
await visit(urls.credentials);
|
||||
} catch (e) {
|
||||
assert.dom(commonSelectors.MODAL_WARNING).isVisible();
|
||||
|
||||
await click(commonSelectors.MODAL_WARNING_CANCEL_BTN, 'Click Cancel');
|
||||
|
||||
assert.strictEqual(currentURL(), urls.passwordCredential);
|
||||
assert
|
||||
.dom(commonSelectors.FIELD_NAME)
|
||||
.hasValue(commonSelectors.FIELD_NAME_VALUE);
|
||||
assert.strictEqual(
|
||||
this.server.schema.credentials.where({
|
||||
type: TYPE_CREDENTIAL_PASSWORD,
|
||||
}).models[0].name,
|
||||
credentialName,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('password field renders in edit mode only for a username & password credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
@@ -699,6 +909,26 @@ module(
|
||||
assert.dom(commonSelectors.FIELD_PASSWORD).isVisible();
|
||||
});
|
||||
|
||||
test('password field renders in edit mode only for a password credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
'color-contrast': {
|
||||
// [ember-a11y-ignore]: axe rule "color-contrast" automatically ignored on 2025-11-06
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await visit(urls.passwordCredential);
|
||||
|
||||
assert.dom(commonSelectors.FIELD_PASSWORD).doesNotExist();
|
||||
|
||||
await click(commonSelectors.EDIT_BTN);
|
||||
|
||||
assert.strictEqual(currentURL(), urls.passwordCredential);
|
||||
assert.dom(commonSelectors.FIELD_PASSWORD).isVisible();
|
||||
});
|
||||
|
||||
test('private_key and private_key_passphrase fields render in edit mode only for a username & key pair credential', async function (assert) {
|
||||
setRunOptions({
|
||||
rules: {
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
|
||||
TYPE_CREDENTIAL_JSON,
|
||||
TYPE_CREDENTIAL_PASSWORD,
|
||||
} from 'api/models/credential';
|
||||
import {
|
||||
TYPE_CREDENTIAL_LIBRARY_VAULT_GENERIC,
|
||||
@@ -56,6 +57,7 @@ module('Acceptance | targets | brokered credential sources', function (hooks) {
|
||||
credentialLibrary: null,
|
||||
credential: null,
|
||||
jsonCredential: null,
|
||||
passwordCredential: null,
|
||||
addBrokeredCredentialSourcesForTCPTarget: null,
|
||||
brokeredCredentialSourcesForTCPTarget: null,
|
||||
addBrokeredCredentialSourcesForRDPTarget: null,
|
||||
@@ -80,7 +82,7 @@ module('Acceptance | targets | brokered credential sources', function (hooks) {
|
||||
type: 'static',
|
||||
scope: instances.scopes.project,
|
||||
});
|
||||
instances.credentials = this.server.createList('credential', 4, {
|
||||
instances.credentials = this.server.createList('credential', 5, {
|
||||
scope: instances.scopes.project,
|
||||
credentialStore: instances.staticCredentialStore,
|
||||
});
|
||||
@@ -132,6 +134,7 @@ module('Acceptance | targets | brokered credential sources', function (hooks) {
|
||||
urls.credentialLibrary = `${urls.projectScope}/credential-stores/${instances.credentialLibrary.credentialStoreId}/credential-libraries/${instances.credentialLibrary.id}`;
|
||||
urls.credential = `${urls.projectScope}/credential-stores/${instances.credential.credentialStoreId}/credentials/${instances.credential.id}`;
|
||||
urls.jsonCredential = `${urls.projectScope}/credential-stores/${instances.credentials[3].credentialStoreId}/credentials/${instances.credentials[3].id}`;
|
||||
urls.passwordCredential = `${urls.projectScope}/credential-stores/${instances.credentials[4].credentialStoreId}/credentials/${instances.credentials[4].id}`;
|
||||
urls.addBrokeredCredentialSourcesForTCPTarget = `${urls.tcpTarget}/add-brokered-credential-sources`;
|
||||
urls.addBrokeredCredentialSourcesForRDPTarget = `${urls.rdpTarget}/add-brokered-credential-sources`;
|
||||
getCredentialLibraryCount = () =>
|
||||
@@ -210,6 +213,18 @@ module('Acceptance | targets | brokered credential sources', function (hooks) {
|
||||
link: 'jsonCredential',
|
||||
expectedUrl: 'jsonCredential',
|
||||
},
|
||||
'password credential type for TCP target': {
|
||||
route: 'brokeredCredentialSourcesForTCPTarget',
|
||||
targetName: 'tcpTarget',
|
||||
link: 'passwordCredential',
|
||||
expectedUrl: 'passwordCredential',
|
||||
},
|
||||
'password credential type for RDP target': {
|
||||
route: 'brokeredCredentialSourcesForRDPTarget',
|
||||
targetName: 'rdpTarget',
|
||||
link: 'passwordCredential',
|
||||
expectedUrl: 'passwordCredential',
|
||||
},
|
||||
},
|
||||
async function (assert, input) {
|
||||
setRunOptions({
|
||||
@@ -427,6 +442,11 @@ module('Acceptance | targets | brokered credential sources', function (hooks) {
|
||||
action: commonSelectors.SAVE_BTN,
|
||||
expectedCount: 1,
|
||||
},
|
||||
'save password credential': {
|
||||
credentialSources: [TYPE_CREDENTIAL_PASSWORD],
|
||||
action: commonSelectors.SAVE_BTN,
|
||||
expectedCount: 1,
|
||||
},
|
||||
'save credentials and credential-libraries': {
|
||||
credentialSources: [
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD,
|
||||
@@ -530,6 +550,11 @@ module('Acceptance | targets | brokered credential sources', function (hooks) {
|
||||
action: commonSelectors.SAVE_BTN,
|
||||
expectedCount: 1,
|
||||
},
|
||||
'save password credential': {
|
||||
credentialSources: [TYPE_CREDENTIAL_PASSWORD],
|
||||
action: commonSelectors.SAVE_BTN,
|
||||
expectedCount: 1,
|
||||
},
|
||||
'save credentials and credential-libraries': {
|
||||
credentialSources: [
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD,
|
||||
@@ -572,7 +597,11 @@ module('Acceptance | targets | brokered credential sources', function (hooks) {
|
||||
action: commonSelectors.CANCEL_BTN,
|
||||
expectedCount: 0,
|
||||
},
|
||||
|
||||
'cancel password credential': {
|
||||
credentialSources: [TYPE_CREDENTIAL_PASSWORD],
|
||||
action: commonSelectors.CANCEL_BTN,
|
||||
expectedCount: 0,
|
||||
},
|
||||
'cancel credentials and credential-libraries': {
|
||||
credentialSources: [
|
||||
TYPE_CREDENTIAL_USERNAME_PASSWORD,
|
||||
|
||||
Reference in new issue
Block a user