From bde8b3b283418d4aec3f4a68c1812e3fc263af96 Mon Sep 17 00:00:00 2001 From: Angel Garbarino Date: Thu, 11 Jan 2024 10:55:26 -0800 Subject: [PATCH] Prep for Ember data: misc #2 (#24791) * consolidate policies tests, remove page object for policies in favor of string selectors * auth list test fix * clean up pki-configuration test and only use error.errors if contents are string * use mirage for version on landing page dashboard test * changes not needed for upgrade but good clean up work. * revert pki workflow changes * remove unused test selector * remove change to keep cleaner file count. * add check on control groups * remove space causing test problems * use uuid for database backend name --------- Co-authored-by: Chelsea Shaw --- ui/app/utils/error-message.js | 2 +- ui/mirage/handlers/base.js | 8 + ui/tests/acceptance/auth-list-test.js | 6 +- ui/tests/acceptance/dashboard-test.js | 18 +-- ui/tests/acceptance/policies-acl-old-test.js | 91 ------------ ui/tests/acceptance/policies/index-test.js | 140 ++++++++++++++---- ui/tests/pages/components/console/ui-panel.js | 4 + ui/tests/pages/policies/create.js | 9 -- ui/tests/pages/policies/index.js | 21 --- 9 files changed, 139 insertions(+), 160 deletions(-) delete mode 100644 ui/tests/acceptance/policies-acl-old-test.js delete mode 100644 ui/tests/pages/policies/create.js delete mode 100644 ui/tests/pages/policies/index.js diff --git a/ui/app/utils/error-message.js b/ui/app/utils/error-message.js index c8c0cbf6f3ea..62cb692fd66e 100644 --- a/ui/app/utils/error-message.js +++ b/ui/app/utils/error-message.js @@ -5,7 +5,7 @@ // accepts an error and returns error.errors joined with a comma, error.message or a fallback message export default function (error, fallbackMessage = 'An error occurred, please try again') { - if (error instanceof Error && error?.errors) { + if (error instanceof Error && error?.errors && typeof error.errors[0] === 'string') { return error.errors.join(', '); } return error?.message || fallbackMessage; diff --git a/ui/mirage/handlers/base.js b/ui/mirage/handlers/base.js index 52a7ff996a5c..92ebdbda44e2 100644 --- a/ui/mirage/handlers/base.js +++ b/ui/mirage/handlers/base.js @@ -37,6 +37,14 @@ export default function (server) { }; }); + server.get('/sys/internal/ui/version', function () { + return { + data: { + version: '1.9.0+ent', + }, + }; + }); + server.get('/sys/license/status', function () { return { data: { diff --git a/ui/tests/acceptance/auth-list-test.js b/ui/tests/acceptance/auth-list-test.js index 450b9c95eb26..ea311a3c04e7 100644 --- a/ui/tests/acceptance/auth-list-test.js +++ b/ui/tests/acceptance/auth-list-test.js @@ -34,14 +34,12 @@ module('Acceptance | auth backend list', function (hooks) { this.user1 = 'user1'; this.user2 = 'user2'; - await runCmd(mountAuthCmd('userpass', this.path1)); - await runCmd(mountAuthCmd('userpass', this.path2)); + await runCmd([mountAuthCmd('userpass', this.path1), mountAuthCmd('userpass', this.path2)], false); }); hooks.afterEach(async function () { await authPage.login(); - await runCmd(deleteAuthCmd(this.path1)); - await runCmd(deleteAuthCmd(this.path2)); + await runCmd([deleteAuthCmd(this.path1), deleteAuthCmd(this.path2)], false); return; }); diff --git a/ui/tests/acceptance/dashboard-test.js b/ui/tests/acceptance/dashboard-test.js index e7c3cbed92dd..5c139ca4e19f 100644 --- a/ui/tests/acceptance/dashboard-test.js +++ b/ui/tests/acceptance/dashboard-test.js @@ -28,6 +28,7 @@ import { formatNumber } from 'core/helpers/format-number'; import { pollCluster } from 'vault/tests/helpers/poll-cluster'; import { disableReplication } from 'vault/tests/helpers/replication'; import connectionPage from 'vault/tests/pages/secrets/backend/database/connection'; +import { v4 as uuidv4 } from 'uuid'; import { SELECTORS } from 'vault/tests/helpers/components/dashboard/dashboard-selectors'; @@ -48,10 +49,8 @@ module('Acceptance | landing page dashboard', function (hooks) { await authPage.login(); await visit('/vault/dashboard'); const version = this.owner.lookup('service:version'); - const versionName = version.version; - const versionText = version.isEnterprise - ? `Vault v${versionName.slice(0, versionName.indexOf('+'))} root` - : `Vault v${versionName}`; + // Since we're using mirage, version is mocked static value + const versionText = version.isEnterprise ? `Vault v1.9.0 root` : `Vault v1.9.0`; assert.dom(SELECTORS.cardHeader('Vault version')).hasText(versionText); }); @@ -308,10 +307,11 @@ module('Acceptance | landing page dashboard', function (hooks) { }; test('shows the correct actions and links associated with database', async function (assert) { - await mountSecrets.enable('database', 'database'); - await newConnection('database'); + const databaseBackend = `database-${uuidv4()}`; + await mountSecrets.enable('database', databaseBackend); + await newConnection(databaseBackend); await runCommands([ - `write database/roles/my-role \ + `write ${databaseBackend}/roles/my-role \ db_name=mongodb-database-plugin \ creation_statements='{ "db": "admin", "roles": [{ "role": "readWrite" }, {"role": "read", "db": "foo"}] }' \ default_ttl="1h" \ @@ -319,7 +319,7 @@ module('Acceptance | landing page dashboard', function (hooks) { ]); await settled(); await visit('/vault/dashboard'); - await selectChoose(SELECTORS.searchSelect('secrets-engines'), 'database'); + await selectChoose(SELECTORS.searchSelect('secrets-engines'), databaseBackend); await fillIn(SELECTORS.selectEl, 'Generate credentials for database'); assert.dom(SELECTORS.emptyState('quick-actions')).doesNotExist(); assert.dom(SELECTORS.subtitle('param')).hasText('Role to use'); @@ -327,7 +327,7 @@ module('Acceptance | landing page dashboard', function (hooks) { await selectChoose(SELECTORS.searchSelect('params'), '.ember-power-select-option', 0); await click(SELECTORS.actionButton('Generate credentials')); assert.strictEqual(currentRouteName(), 'vault.cluster.secrets.backend.credentials'); - await consoleComponent.runCommands(deleteEngineCmd('database')); + await consoleComponent.runCommands(deleteEngineCmd(databaseBackend)); }); test('shows the correct actions and links associated with kv v1', async function (assert) { diff --git a/ui/tests/acceptance/policies-acl-old-test.js b/ui/tests/acceptance/policies-acl-old-test.js deleted file mode 100644 index 35eccecf4bc6..000000000000 --- a/ui/tests/acceptance/policies-acl-old-test.js +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { click, fillIn, find, currentURL, waitUntil } from '@ember/test-helpers'; -import { module, test } from 'qunit'; -import { setupApplicationTest } from 'ember-qunit'; -import { v4 as uuidv4 } from 'uuid'; - -import page from 'vault/tests/pages/policies/index'; -import authPage from 'vault/tests/pages/auth'; - -module('Acceptance | policies (old)', function (hooks) { - setupApplicationTest(hooks); - - hooks.beforeEach(function () { - this.uid = uuidv4(); - return authPage.login(); - }); - - test('policies', async function (assert) { - const policyString = 'path "*" { capabilities = ["update"]}'; - const policyName = `Policy test ${this.uid}`; - const policyLower = policyName.toLowerCase(); - - await page.visit({ type: 'acl' }); - // new policy creation - await click('[data-test-policy-create-link]'); - - await fillIn('[data-test-policy-input="name"]', policyName); - await click('[data-test-policy-save]'); - assert - .dom('[data-test-message-error]') - .hasText(`Error 'policy' parameter not supplied or empty`, 'renders error message on save'); - find('.CodeMirror').CodeMirror.setValue(policyString); - await click('[data-test-policy-save]'); - - await waitUntil(() => currentURL() === `/vault/policy/acl/${encodeURIComponent(policyLower)}`); - assert.strictEqual( - currentURL(), - `/vault/policy/acl/${encodeURIComponent(policyLower)}`, - 'navigates to policy show on successful save' - ); - assert.dom('[data-test-policy-name]').hasText(policyLower, 'displays the policy name on the show page'); - assert.dom('[data-test-flash-message].is-info').doesNotExist('no flash message is displayed on save'); - await click('[data-test-policy-list-link] a'); - await fillIn('[data-test-component="navigate-input"]', policyLower); - assert - .dom(`[data-test-policy-link="${policyLower}"]`) - .exists({ count: 1 }, 'new policy shown in the list'); - - // policy deletion - await click(`[data-test-policy-link="${policyLower}"]`); - - await click('[data-test-policy-edit-toggle]'); - - await click('[data-test-confirm-action-trigger]'); - - await click('[data-test-confirm-button]'); - await waitUntil(() => currentURL() === `/vault/policies/acl`); - assert.strictEqual( - currentURL(), - `/vault/policies/acl`, - 'navigates to policy list on successful deletion' - ); - await fillIn('[data-test-component="navigate-input"]', policyLower); - assert - .dom(`[data-test-policy-item="${policyLower}"]`) - .doesNotExist('deleted policy is not shown in the list'); - }); - - // https://github.com/hashicorp/vault/issues/4395 - test('it properly fetches policies when the name ends in a ,', async function (assert) { - const policyString = 'path "*" { capabilities = ["update"]}'; - const policyName = `${this.uid}-policy-symbol,.`; - - await page.visit({ type: 'acl' }); - // new policy creation - await click('[data-test-policy-create-link]'); - - await fillIn('[data-test-policy-input="name"]', policyName); - find('.CodeMirror').CodeMirror.setValue(policyString); - await click('[data-test-policy-save]'); - assert.ok( - await waitUntil(() => currentURL() === `/vault/policy/acl/${policyName}`), - 'navigates to policy show on successful save' - ); - assert.dom('[data-test-policy-edit-toggle]').exists({ count: 1 }, 'shows the edit toggle'); - }); -}); diff --git a/ui/tests/acceptance/policies/index-test.js b/ui/tests/acceptance/policies/index-test.js index bda75c118093..e21ce2b5871d 100644 --- a/ui/tests/acceptance/policies/index-test.js +++ b/ui/tests/acceptance/policies/index-test.js @@ -3,38 +3,58 @@ * SPDX-License-Identifier: BUSL-1.1 */ -import { currentURL, currentRouteName, settled, fillIn } from '@ember/test-helpers'; +import { + currentURL, + currentRouteName, + settled, + fillIn, + visit, + click, + find, + waitFor, + waitUntil, +} from '@ember/test-helpers'; import { module, test } from 'qunit'; import { setupApplicationTest } from 'ember-qunit'; -import { create } from 'ember-cli-page-object'; +import { v4 as uuidv4 } from 'uuid'; -import page from 'vault/tests/pages/policies/index'; import authPage from 'vault/tests/pages/auth'; -import consoleClass from 'vault/tests/pages/components/console/ui-panel'; - -const consoleComponent = create(consoleClass); +import { runCmd } from 'vault/tests/helpers/commands'; +const SELECT = { + policyByName: (name) => `[data-test-policy-link="${name}"]`, + filterBar: '[data-test-component="navigate-input"]', + delete: '[data-test-confirm-action-trigger]', + confirmDelete: '[data-test-confirm-button]', + createLink: '[data-test-policy-create-link]', + nameInput: '[data-test-policy-input="name"]', + save: '[data-test-policy-save]', + createError: '[data-test-message-error]', + policyTitle: '[data-test-policy-name]', + listBreadcrumb: '[data-test-policy-list-link] a', +}; module('Acceptance | policies/acl', function (hooks) { setupApplicationTest(hooks); hooks.beforeEach(function () { + this.uid = uuidv4(); return authPage.login(); }); test('it lists default and root acls', async function (assert) { - await page.visit({ type: 'acl' }); + await visit('/vault/policies/acl'); assert.strictEqual(currentURL(), '/vault/policies/acl'); - await fillIn('[data-test-component="navigate-input"]', 'default'); - assert.ok(page.findPolicyByName('default'), 'default policy shown in the list'); - await fillIn('[data-test-component="navigate-input"]', 'root'); - assert.ok(page.findPolicyByName('root'), 'root policy shown in the list'); + await fillIn(SELECT.filterBar, 'default'); + await waitFor(SELECT.policyByName('default')); + assert.dom(SELECT.policyByName('default')).exists('default policy shown in the list'); + await fillIn(SELECT.filterBar, 'root'); + // root isn't clickable so it has a different selector + assert.dom('[data-test-policy-name]').hasText('root', 'root policy shown in the list'); }); test('it navigates to show when clicking on the link', async function (assert) { - await page.visit({ type: 'acl' }); - await settled(); - await page.findPolicyByName('default').click(); - await settled(); + await visit('/vault/policies/acl'); + await click(SELECT.policyByName('default')); assert.strictEqual(currentRouteName(), 'vault.cluster.policy.show'); assert.strictEqual(currentURL(), '/vault/policy/acl/default'); }); @@ -42,16 +62,86 @@ module('Acceptance | policies/acl', function (hooks) { test('it allows deletion of policies with dots in names', async function (assert) { const POLICY = 'path "*" { capabilities = ["list"]}'; const policyName = 'list.policy'; - await consoleComponent.runCommands([`write sys/policies/acl/${policyName} policy=${btoa(POLICY)}`]); - await settled(); - await page.visit({ type: 'acl' }); - await settled(); - const policy = page.row.filterBy('name', policyName)[0]; - assert.ok(policy, 'policy is shown in the list'); - await policy.menu(); + await runCmd(`write sys/policies/acl/${policyName} policy=${window.btoa(POLICY)}`); await settled(); - await page.delete().confirmDelete(); - await settled(); - assert.notOk(page.findPolicyByName(policyName), 'policy is deleted successfully'); + await visit('/vault/policies/acl'); + assert.dom(SELECT.policyByName(policyName)).exists('policy is shown in list'); + await click(`${SELECT.policyByName(policyName)} [data-test-popup-menu-trigger]`); + await click(SELECT.delete); + await click(SELECT.confirmDelete); + assert.dom(SELECT.policyByName(policyName)).doesNotExist('policy is deleted successfully'); + }); + + // https://github.com/hashicorp/vault/issues/4395 + test('it properly fetches policies when the name ends in a ,', async function (assert) { + const policyString = 'path "*" { capabilities = ["update"]}'; + const policyName = `${this.uid}-policy-symbol,.`; + + await visit('/vault/policies/acl'); + // new policy creation + await click(SELECT.createLink); + + await fillIn(SELECT.nameInput, policyName); + find('.CodeMirror').CodeMirror.setValue(policyString); + await click(SELECT.save); + assert.strictEqual( + currentURL(), + `/vault/policy/acl/${policyName}`, + 'navigates to policy show on successful save' + ); + assert.dom('[data-test-policy-edit-toggle]').exists({ count: 1 }, 'shows the edit toggle'); + }); + + test('it can create and delete correctly', async function (assert) { + const policyString = 'path "*" { capabilities = ["update"]}'; + const policyName = `Policy test ${this.uid}`; + const policyLower = policyName.toLowerCase(); + + await visit('/vault/policies/acl'); + // new policy creation + await click(SELECT.createLink); + + await fillIn(SELECT.nameInput, policyName); + await click(SELECT.save); + assert + .dom(SELECT.createError) + .hasText(`Error 'policy' parameter not supplied or empty`, 'renders error message on save'); + find('.CodeMirror').CodeMirror.setValue(policyString); + await click(SELECT.save); + + await waitUntil(() => currentURL() === `/vault/policy/acl/${encodeURIComponent(policyLower)}`); + assert.strictEqual( + currentURL(), + `/vault/policy/acl/${encodeURIComponent(policyLower)}`, + 'navigates to policy show on successful save' + ); + assert.dom(SELECT.policyTitle).hasText(policyLower, 'displays the policy name on the show page'); + assert.dom('[data-test-flash-message].is-info').doesNotExist('no flash message is displayed on save'); + await click(SELECT.listBreadcrumb); + + assert.strictEqual(currentURL(), `/vault/policies/acl`, 'navigates to policy list from breadcrumb'); + // List of policies can get long quickly -- filter for the policy to make the test more robust + await fillIn(SELECT.filterBar, policyLower); + assert + .dom(`[data-test-policy-link="${policyLower}"]`) + .exists({ count: 1 }, 'new policy shown in the list'); + + // policy deletion + await click(SELECT.policyByName(policyLower)); + + await click('[data-test-policy-edit-toggle]'); + + await click('[data-test-confirm-action-trigger]'); + + await click('[data-test-confirm-button]'); + await waitUntil(() => currentURL() === `/vault/policies/acl`); + assert.strictEqual( + currentURL(), + `/vault/policies/acl`, + 'navigates to policy list on successful deletion' + ); + assert + .dom(`[data-test-policy-item="${policyLower}"]`) + .doesNotExist('deleted policy is not shown in the list'); }); }); diff --git a/ui/tests/pages/components/console/ui-panel.js b/ui/tests/pages/components/console/ui-panel.js index c44b8a514158..3bb6503c3157 100644 --- a/ui/tests/pages/components/console/ui-panel.js +++ b/ui/tests/pages/components/console/ui-panel.js @@ -31,6 +31,10 @@ export default { }), lastTextOutput: getter(function () { const count = this.logTextItems.length; + if (count === 0) { + // If no logOutput items are found, we can assume the response is empty + return ''; + } return this.logTextItems.objectAt(count - 1).text; }), logJSONItems: collection('[data-test-component="console/log-json"]', { diff --git a/ui/tests/pages/policies/create.js b/ui/tests/pages/policies/create.js deleted file mode 100644 index 89543ecd6272..000000000000 --- a/ui/tests/pages/policies/create.js +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { create, visitable } from 'ember-cli-page-object'; -export default create({ - visit: visitable('/vault/policies/create'), -}); diff --git a/ui/tests/pages/policies/index.js b/ui/tests/pages/policies/index.js deleted file mode 100644 index f82c0e2bc91b..000000000000 --- a/ui/tests/pages/policies/index.js +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Copyright (c) HashiCorp, Inc. - * SPDX-License-Identifier: BUSL-1.1 - */ - -import { text, create, collection, clickable, visitable } from 'ember-cli-page-object'; -export default create({ - visit: visitable('/vault/policies/:type'), - policies: collection('[data-test-policy-item]', { - name: text('[data-test-policy-name]'), - }), - row: collection('[data-test-policy-link]', { - name: text(), - menu: clickable('[data-test-popup-menu-trigger]'), - }), - findPolicyByName(name) { - return this.policies.filterBy('name', name)[0]; - }, - delete: clickable('[data-test-confirm-action-trigger]'), - confirmDelete: clickable('[data-test-confirm-button]'), -});