Fix failing ent test (#30985)

* fix transform submit issues

* fix and clean up replication

* left overs

* build waiter for namespace capabilities check to show button, hoping it helps with test flaky issues
This commit is contained in:
Angel Garbarino 2025-06-16 13:21:12 -06:00 committed by GitHub
parent 17054b52a4
commit 5e6516ad83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 109 additions and 201 deletions

View File

@ -8,6 +8,8 @@ import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';
import { service } from '@ember/service';
import keys from 'core/utils/keys';
import { buildWaiter } from '@ember/test-waiters';
import type Router from 'vault/router';
import type NamespaceService from 'vault/services/namespace';
import type AuthService from 'vault/vault/services/auth';
@ -20,6 +22,8 @@ interface NamespaceOption {
label: string;
}
const waiter = buildWaiter('namespace-picker');
/**
* @module NamespacePicker
* @description component is used to display a dropdown listing all namespaces that the current user has access to.
@ -176,6 +180,7 @@ export default class NamespacePicker extends Component {
@action
async fetchListCapability(): Promise<void> {
const waiterToken = waiter.beginAsync();
try {
const namespacePermission = await this.store.findRecord('capabilities', 'sys/namespaces/');
this.canRefreshNamespaces = namespacePermission.get('canList');
@ -183,6 +188,8 @@ export default class NamespacePicker extends Component {
} catch (error) {
// If the findRecord call fails, the user lacks permissions to refresh or manage namespaces.
this.canRefreshNamespaces = this.canManageNamespaces = false;
} finally {
waiter.endAsync(waiterToken);
}
}

View File

@ -70,11 +70,7 @@
</div>
<div class="field is-grouped-split box is-fullwidth is-bottomless">
<Hds::ButtonSet>
<Hds::Button
@text={{if (eq @mode "create") "Create template" "Save"}}
type="submit"
data-test-template-transform-create
/>
<Hds::Button @text={{if (eq @mode "create") "Create template" "Save"}} type="submit" data-test-submit />
{{#if (eq @mode "create")}}
<Hds::Button
@text="Cancel"

View File

@ -13,7 +13,7 @@
</div>
<div class="field is-grouped-split box is-fullwidth is-bottomless">
<Hds::ButtonSet>
<Hds::Button @text="Create transformation" type="submit" data-test-transform-create />
<Hds::Button @text="Create transformation" type="submit" data-test-submit />
<Hds::Button
@text="Cancel"
@color="secondary"

View File

@ -44,7 +44,7 @@
</div>
<div class="field is-grouped-split box is-fullwidth is-bottomless">
<Hds::ButtonSet>
<Hds::Button @text="Save" type="submit" data-test-transformation-save-button />
<Hds::Button @text="Save" type="submit" data-test-submit />
<Hds::Button
@text="Cancel"
@color="secondary"

View File

@ -70,11 +70,7 @@
</div>
<div class="field is-grouped-split box is-fullwidth is-bottomless">
<Hds::ButtonSet>
<Hds::Button
@text={{if (eq this.mode "create") "Create role" "Save"}}
type="submit"
data-test-role-transform-create
/>
<Hds::Button @text={{if (eq this.mode "create") "Create role" "Save"}} type="submit" data-test-submit />
{{#if (eq this.mode "create")}}
<Hds::Button
@text="Cancel"

View File

@ -25,7 +25,7 @@
name="activation-token-id"
id="activation-token-id"
@value={{this.id}}
data-test-replication-secondary-id
data-test-input="Secondary ID"
/>
</div>
<p class="help has-text-grey">
@ -84,7 +84,7 @@
<M.Footer>
<Hds::ButtonSet>
<Hds::Copy::Button
data-test-modal-copy
data-test-button="Copy token"
@text="Copy token"
@textToCopy={{this.token}}
class="primary"
@ -95,7 +95,7 @@
{{on "click" (action "onCopy")}}
/>
<Hds::Button
data-test-modal-close
data-test-cancel
disabled={{not this.isTokenCopied}}
@text="Close"
@color="secondary"

View File

@ -40,7 +40,7 @@
<dd.Interactive
@route="mode.secondaries.config-show"
@models={{array this.replicationMode secondary}}
data-test-replication-path-filter-link={{true}}
data-test-replication-path-filter-link
>Path filter config</dd.Interactive>
{{/if}}
{{#if this.model.canRevokeSecondary}}

View File

@ -14,7 +14,13 @@
Secondary ID
</label>
<div class="control">
<Input class="input" name="activation-token-id" id="activation-token-id" @value={{this.id}} />
<Input
class="input"
name="activation-token-id"
id="activation-token-id"
@value={{this.id}}
data-test-input="Secondary ID"
/>
</div>
<p class="help has-text-grey">
The secondary id to revoke; given initially to generate a secondary token.

View File

@ -3,31 +3,14 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import { clickTrigger } from 'ember-power-select/test-support/helpers';
import {
click,
fillIn,
findAll,
currentURL,
find,
visit,
settled,
waitUntil,
waitFor,
} from '@ember/test-helpers';
import { click, fillIn, findAll, currentURL, visit, settled, waitFor } from '@ember/test-helpers';
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { login } from 'vault/tests/helpers/auth/auth-helpers';
import { pollCluster } from 'vault/tests/helpers/poll-cluster';
import { create } from 'ember-cli-page-object';
import flashMessage from 'vault/tests/pages/components/flash-message';
import ss from 'vault/tests/pages/components/search-select';
import { disableReplication } from 'vault/tests/helpers/replication';
import { addSecondary, disableReplication, enableReplication } from 'vault/tests/helpers/replication';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
const searchSelect = create(ss);
const flash = create(flashMessage);
module('Acceptance | Enterprise | replication', function (hooks) {
setupApplicationTest(hooks);
@ -47,85 +30,48 @@ module('Acceptance | Enterprise | replication', function (hooks) {
await settled();
});
test('replication', async function (assert) {
assert.expect(18);
const secondaryName = 'firstSecondary';
const mode = 'deny';
// confirm unable to visit dr secondary details page when both replications are disabled
test('it shows DR empty state when DR is not configured', async function (assert) {
await visit('/vault/replication-dr-promote/details');
assert.dom('[data-test-component="empty-state"]').exists();
assert
.dom('[data-test-empty-state-title]')
.dom(GENERAL.emptyStateTitle)
.includesText('Disaster Recovery secondary not set up', 'shows the correct title of the empty state');
assert
.dom('[data-test-empty-state-message]')
.dom(GENERAL.emptyStateMessage)
.hasText(
'This cluster has not been enabled as a Disaster Recovery Secondary. You can do so by enabling replication and adding a secondary from the Disaster Recovery Primary.',
'renders default message specific to when no replication is enabled'
);
});
await visit('/vault/replication');
assert.strictEqual(currentURL(), '/vault/replication');
// enable perf replication
await click('[data-test-replication-type-select="performance"]');
await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
await click(GENERAL.submitButton);
test('Performance replication: add secondary and delete config', async function (assert) {
const secondaryName = `performanceSecondary`;
const mode = 'deny';
await enableReplication('performance', 'primary');
await pollCluster(this.owner);
await settled();
// confirm that the details dashboard shows
assert.ok(await waitUntil(() => find('[data-test-replication-dashboard]')), 'details dashboard is shown');
// add a secondary with a mount filter config
await click('[data-test-replication-link="secondaries"]');
await click('[data-test-secondary-add]');
await fillIn('[data-test-replication-secondary-id]', secondaryName);
await click('#deny');
await clickTrigger();
await searchSelect.options.objectAt(0).click();
const mountPath = find('[data-test-selected-option="0"]').innerText?.trim();
await click('[data-test-secondary-add]');
await waitFor('[data-test-replication-dashboard]', 2000);
await addSecondary(secondaryName, mode);
await pollCluster(this.owner);
// click into the added secondary's mount filter config
await settled();
await click('[data-test-replication-link="secondaries"]');
await click(GENERAL.menuTrigger);
await click('[data-test-replication-path-filter-link]');
assert.strictEqual(
currentURL(),
`/vault/replication/performance/secondaries/config/show/${secondaryName}`
);
assert.dom('[data-test-mount-config-mode]').includesText(mode, 'show page renders the correct mode');
assert
.dom('[data-test-mount-config-paths]')
.hasTextContaining(`${mountPath}`, 'show page renders the correct mount path');
// delete config by choosing "no filter" in the edit screen
// delete config by choosing "no filter" on the edit screen
await click('[data-test-replication-link="edit-mount-config"]');
await click('#no-filtering');
await click('[data-test-config-save]');
await settled(); // eslint-disable-line
assert.strictEqual(
flash.latestMessage,
`The performance mount filter config for the secondary ${secondaryName} was successfully deleted.`,
'renders success flash upon deletion'
);
assert.strictEqual(
currentURL(),
`/vault/replication/performance/secondaries`,
@ -137,36 +83,14 @@ module('Acceptance | Enterprise | replication', function (hooks) {
assert
.dom(`[data-test-secondaries-node=${secondaryName}]`)
.exists('shows a table row the recently added secondary');
});
// nav to DR
await visit('/vault/replication/dr');
test('DR replication: enable and add secondary', async function (assert) {
const secondaryName = 'drSecondary';
await fillIn('[data-test-replication-cluster-mode-select]', 'secondary');
assert
.dom('[data-test-replication-enable]')
.isDisabled('dr secondary enable is disabled when other replication modes are on');
// disable performance replication
await disableReplication('performance', assert);
await enableReplication('dr', 'primary');
await pollCluster(this.owner);
await settled();
await pollCluster(this.owner);
// enable dr replication
await visit('vault/replication/dr');
await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
await click('button[type="submit"]');
await pollCluster(this.owner);
await waitUntil(() => find('[data-test-empty-state-title]'));
// empty state inside of know secondaries table
assert
.dom('[data-test-empty-state-title]')
.includesText(
'No known dr secondary clusters associated with this cluster',
'shows the correct title of the empty state'
);
assert
.dom('[data-test-replication-title="Disaster Recovery"]')
.includesText('Disaster Recovery', 'it displays the replication type correctly');
@ -174,18 +98,11 @@ module('Acceptance | Enterprise | replication', function (hooks) {
.dom('[data-test-replication-mode-display]')
.includesText('primary', 'it displays the cluster mode correctly');
// add dr secondary
await click('[data-test-replication-link="secondaries"]');
await click('[data-test-secondary-add]');
await fillIn('[data-test-replication-secondary-id]', secondaryName);
await click('[data-test-secondary-add]');
await addSecondary(secondaryName);
await pollCluster(this.owner);
await click('[data-test-replication-link="secondaries"]');
await settled();
await click('[data-test-replication-link="secondaries"]');
assert
.dom('[data-test-secondary-name]')
.includesText(secondaryName, 'it displays the secondary in the list of known secondaries');
@ -237,52 +154,38 @@ module('Acceptance | Enterprise | replication', function (hooks) {
test('add secondary and navigate through token generation modal', async function (assert) {
const secondaryNameFirst = 'firstSecondary';
const secondaryNameSecond = 'secondSecondary';
await visit('/vault/replication');
// enable perf replication
await click('[data-test-replication-type-select="performance"]');
await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
await click(GENERAL.submitButton);
await enableReplication('performance', 'primary');
await pollCluster(this.owner);
await settled();
// add a secondary with default TTL
await click('[data-test-replication-link="secondaries"]');
await click('[data-test-secondary-add]');
await fillIn('[data-test-replication-secondary-id]', secondaryNameFirst);
await click('[data-test-secondary-add]');
// confirm that the details dashboard shows
await addSecondary(secondaryNameFirst);
await pollCluster(this.owner);
await settled();
// checks on secondary token modal
assert.dom('#replication-copy-token-modal').exists();
assert.dom('[data-test-inline-error-message]').hasText('Copy token to dismiss modal');
assert.dom('[data-test-row-value="TTL"]').hasText('1800s', 'shows the correct TTL of 1800s');
assert.dom(GENERAL.inlineError).hasText('Copy token to dismiss modal');
assert.dom(GENERAL.infoRowValue('TTL')).hasText('1800s', 'shows the correct TTL of 1800s');
// click off the modal to make sure you don't just have to click on the copy-close button to copy the token
assert.dom('[data-test-modal-close]').isDisabled('cancel is disabled');
await click('[data-test-modal-copy]');
assert.dom('[data-test-modal-close]').isEnabled('cancel is enabled after token is copied');
await click('[data-test-modal-close]');
assert.dom(GENERAL.cancelButton).isDisabled('cancel is disabled');
await click(GENERAL.button('Copy token'));
assert.dom(GENERAL.cancelButton).isEnabled('cancel is enabled after token is copied');
await click(GENERAL.cancelButton);
// add another secondary not using the default ttl
await click('[data-test-secondary-add]');
await fillIn('[data-test-replication-secondary-id]', secondaryNameSecond);
await fillIn('[data-test-input="Secondary ID"]', secondaryNameSecond);
await click(GENERAL.toggleInput('Time to Live (TTL) for generated secondary token'));
await fillIn('[data-test-ttl-value]', 3);
await click('[data-test-secondary-add]');
await pollCluster(this.owner);
await settled();
assert.dom('[data-test-row-value="TTL"]').hasText('180s', 'shows the correct TTL of 180s');
await click('[data-test-modal-copy]');
await click('[data-test-modal-close]');
assert.dom(GENERAL.infoRowValue('TTL')).hasText('180s', 'shows the correct TTL of 180s');
await click(GENERAL.button('Copy token'));
await click(GENERAL.cancelButton);
// confirm you were redirected to the secondaries page
assert.strictEqual(
@ -297,12 +200,7 @@ module('Acceptance | Enterprise | replication', function (hooks) {
test('render performance and dr primary and navigate to details page', async function (assert) {
// enable perf primary replication
await visit('/vault/replication');
await click('[data-test-replication-type-select="performance"]');
await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
await click(GENERAL.submitButton);
await enableReplication('performance', 'primary');
await pollCluster(this.owner);
await settled();
@ -342,13 +240,7 @@ module('Acceptance | Enterprise | replication', function (hooks) {
test('render performance secondary and navigate to the details page', async function (assert) {
// enable perf replication
await visit('/vault/replication');
await click('[data-test-replication-type-select="performance"]');
await fillIn('[data-test-replication-cluster-mode-select]', 'primary');
await click(GENERAL.submitButton);
await enableReplication('performance', 'primary');
await pollCluster(this.owner);
await settled();

View File

@ -5,7 +5,7 @@
import { module, test } from 'qunit';
import { setupApplicationTest } from 'ember-qunit';
import { currentURL, click, settled, currentRouteName, visit } from '@ember/test-helpers';
import { click, currentRouteName, currentURL, fillIn, settled, visit } from '@ember/test-helpers';
import { create } from 'ember-cli-page-object';
import { selectChoose } from 'ember-power-select/test-support';
import { typeInSearch, clickTrigger } from 'ember-power-select/test-support/helpers';
@ -14,7 +14,6 @@ import { login } from 'vault/tests/helpers/auth/auth-helpers';
import mountSecrets from 'vault/tests/pages/settings/mount-secret-backend';
import transformationsPage from 'vault/tests/pages/secrets/backend/transform/transformations';
import rolesPage from 'vault/tests/pages/secrets/backend/transform/roles';
import templatesPage from 'vault/tests/pages/secrets/backend/transform/templates';
import alphabetsPage from 'vault/tests/pages/secrets/backend/transform/alphabets';
import searchSelect from 'vault/tests/pages/components/search-select';
import { runCmd } from '../helpers/commands';
@ -59,7 +58,7 @@ const newRole = async (backend, name) => {
module('Acceptance | Enterprise | Transform secrets', function (hooks) {
setupApplicationTest(hooks);
hooks.beforeEach(function () {
hooks.beforeEach(async function () {
return login();
});
@ -103,7 +102,9 @@ module('Acceptance | Enterprise | Transform secrets', function (hooks) {
test('it can create a transformation and add itself to the role attached', async function (assert) {
await visit('/vault/settings/mount-secret-backend');
const backend = `transform-${uuidv4()}`;
await mountBackend('transform', backend);
await click('[data-test-mount-type="transform"]');
await fillIn(GENERAL.inputByAttr('path'), backend);
await click(GENERAL.submitButton);
const transformationName = 'foo';
const roleName = 'foo-role';
await settled();
@ -116,20 +117,19 @@ module('Acceptance | Enterprise | Transform secrets', function (hooks) {
);
await transformationsPage.name(transformationName);
await settled();
assert.dom('[data-test-input="type"]').hasValue('fpe', 'Has type FPE by default');
assert.dom('[data-test-input="tweak_source"]').exists('Shows tweak source when FPE');
assert.dom(GENERAL.inputByAttr('type')).hasValue('fpe', 'Has type FPE by default');
assert.dom(GENERAL.inputByAttr('tweak_source')).exists('Shows tweak source when FPE');
await transformationsPage.type('masking');
await settled();
assert
.dom('[data-test-input="masking_character"]')
.dom(GENERAL.inputByAttr('masking_character'))
.exists('Shows masking character input when changed to masking type');
assert.dom('[data-test-input="tweak_source"]').doesNotExist('Does not show tweak source when masking');
assert.dom(GENERAL.inputByAttr('tweak_source')).doesNotExist('Does not show tweak source when masking');
await clickTrigger('#template');
await settled();
assert.strictEqual(searchSelectComponent.options.length, 2, 'list shows two builtin options by default');
await selectChoose('#template', '.ember-power-select-option', 0);
await settled();
await clickTrigger('#allowed_roles');
await settled();
await typeInSearch(roleName);
@ -228,8 +228,7 @@ module('Acceptance | Enterprise | Transform secrets', function (hooks) {
await settled();
await click('#allowed_roles [data-test-selected-list-button="delete"]');
await transformationsPage.save();
await settled();
await click(GENERAL.submitButton);
assert.dom('.flash-message.is-info').exists('Shows info message since role could not be updated');
assert.strictEqual(
currentURL(),
@ -260,8 +259,8 @@ module('Acceptance | Enterprise | Transform secrets', function (hooks) {
`/vault/secrets/${backend}/create?itemType=template`,
'redirects to create template page'
);
await templatesPage.name(templateName);
await templatesPage.pattern(`(\\d{4})`);
await fillIn(GENERAL.inputByAttr('name'), templateName);
await fillIn(GENERAL.inputByAttr('pattern'), `(\\d{4})`);
await clickTrigger('#alphabet');
await settled();
assert.ok(searchSelectComponent.options.length > 0, 'lists built-in alphabets');
@ -273,8 +272,7 @@ module('Acceptance | Enterprise | Transform secrets', function (hooks) {
`/vault/secrets/${backend}/show/template/${templateName}`,
'redirects to show template page after submit'
);
await templatesPage.editLink();
await settled();
await click('[data-test-edit-link]');
assert.strictEqual(
currentURL(),
`/vault/secrets/${backend}/edit/template/${templateName}`,

View File

@ -4,8 +4,43 @@
*/
import { click, fillIn, findAll, currentURL, visit, settled, waitUntil } from '@ember/test-helpers';
import ss from 'vault/tests/pages/components/search-select';
import { create } from 'ember-cli-page-object';
import { clickTrigger } from 'ember-power-select/test-support/helpers';
import { GENERAL } from 'vault/tests/helpers/general-selectors';
const searchSelect = create(ss);
/**
* Enables replication mode.
* @param {string} type - The replication type ('performance' or 'dr').
* @param {string} mode - The cluster mode ('primary' or 'secondary').
*/
export async function enableReplication(type, mode) {
await visit('/vault/replication');
await click(`[data-test-replication-type-select="${type}"]`);
await fillIn('[data-test-replication-cluster-mode-select]', mode);
await click(GENERAL.submitButton);
}
/**
* Adds a secondary cluster.
* @param {string} secondaryName - The name of the secondary cluster.
* @param {string} mountFilterMode - The mount filter mode ('deny' or 'allow').
*/
export async function addSecondary(secondaryName, mountFilterMode = null) {
await click('[data-test-replication-link="secondaries"]');
await click('[data-test-secondary-add]');
await fillIn('[data-test-input="Secondary ID"]', secondaryName);
if (mountFilterMode) {
await click(`#${mountFilterMode}`);
await clickTrigger();
await searchSelect.options.objectAt(0).click();
}
await click('[data-test-secondary-add]');
}
export const disableReplication = async (type, assert) => {
// disable performance replication
await visit(`/vault/replication/${type}`);

View File

@ -13,6 +13,5 @@ export default create({
createLink: clickable('[data-test-secret-create]'),
name: fillable('[data-test-input="name"]'),
transformations: fillable('[data-test-input="transformations"'),
submit: clickable('[data-test-role-transform-create]'),
modalConfirm: clickable('[data-test-edit-confirm-button]'),
});

View File

@ -1,19 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { create, clickable, fillable, visitable } from 'ember-cli-page-object';
import ListView from 'vault/tests/pages/components/list-view';
export default create({
...ListView,
visit: visitable('/vault/secrets/:backend/list?tab=templates'),
visitCreate: visitable('/vault/secrets/:backend/create?itemType=template'),
editLink: clickable('[data-test-edit-link]'),
name: fillable('[data-test-input="name"]'),
pattern: fillable('[data-test-input="pattern"'),
alphabet: fillable('[data-test-input="alphabet"'),
submit: clickable('[data-test-template-transform-create]'),
removeAlphabet: clickable('#alphabet [data-test-selected-list-button="delete"]'),
});

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: BUSL-1.1
*/
import { create, clickable, fillable, visitable } from 'ember-cli-page-object';
import { create, fillable, visitable } from 'ember-cli-page-object';
import ListView from 'vault/tests/pages/components/list-view';
export default create({
@ -12,9 +12,7 @@ export default create({
visitShow: visitable('/vault/secrets/:backend/show/:id'),
visitCreate: visitable('/vault/secrets/:backend/create'),
name: fillable('[data-test-input="name"]'),
submit: clickable('[data-test-transform-create]'),
type: fillable('[data-test-input="type"'),
tweakSource: fillable('[data-test-input="tweak_source"'),
maskingChar: fillable('[data-test-input="masking_character"'),
save: clickable('[data-test-transformation-save-button]'),
});