From f34a1df29845b4c378659135032ebbeda7b14665 Mon Sep 17 00:00:00 2001 From: Zhihe Li Date: Fri, 15 May 2026 12:40:37 -0400 Subject: [PATCH] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20Update=20e2e=20test?= =?UTF-8?q?=20for=20finding=20code=20editor=20(#3273)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 💡 Update e2e test for finding code editor * test: 💍 Added more acceptance tests --- e2e-tests/admin/pages/storage-buckets.js | 5 +- .../acceptance/roles/edit-grants-test.js | 121 +++++++++++++++++- ui/admin/tests/acceptance/roles/selectors.js | 6 + 3 files changed, 126 insertions(+), 6 deletions(-) diff --git a/e2e-tests/admin/pages/storage-buckets.js b/e2e-tests/admin/pages/storage-buckets.js index 238f3f3cc..dbcbb54ac 100644 --- a/e2e-tests/admin/pages/storage-buckets.js +++ b/e2e-tests/admin/pages/storage-buckets.js @@ -98,7 +98,10 @@ export class StorageBucketsPage extends BaseResourcePage { await this.page.getByLabel('Region').fill(region); await this.page.getByLabel('Access key ID').fill(accessKeyId); await this.page.getByLabel('Secret access key').fill(secretAccessKey); - await this.page.getByLabel('Worker filter').scrollIntoViewIfNeeded(); + await this.page + .getByLabel('Worker filter') + .first() + .scrollIntoViewIfNeeded(); await this.page .getByLabel('Worker filter') .getByRole('textbox') diff --git a/ui/admin/tests/acceptance/roles/edit-grants-test.js b/ui/admin/tests/acceptance/roles/edit-grants-test.js index a04a71ffd..e62e41dc6 100644 --- a/ui/admin/tests/acceptance/roles/edit-grants-test.js +++ b/ui/admin/tests/acceptance/roles/edit-grants-test.js @@ -11,6 +11,7 @@ import { find, select, waitFor, + triggerKeyEvent, } from '@ember/test-helpers'; import { setupApplicationTest } from 'admin/tests/helpers'; import * as selectors from './selectors'; @@ -107,10 +108,12 @@ module('Acceptance | roles/edit grants', function (hooks) { assert.dom(selectors.EXPORT_OPTIONS_FLYOUT).isVisible(); assert.dom(selectors.EXPORT_FORMAT_SELECT).hasValue('terraform'); - // TODO: Update to check for the actual formatted grant string. - assert - .dom(selectors.FORMATTED_EXPORT_CODE_BLOCK) - .includesText('grant_strings'); + assert.dom(selectors.FORMATTED_EXPORT_CODE_BLOCK).hasText(` + grant_strings = [ + "${instances.role.grant_strings[0]}", + "${instances.role.grant_strings[1]}", + ] + `); }); test('user can export a hcl formatted grant string', async function (assert) { @@ -132,7 +135,12 @@ module('Acceptance | roles/edit grants', function (hooks) { assert.dom(selectors.EXPORT_OPTIONS_FLYOUT).isVisible(); assert.dom(selectors.EXPORT_FORMAT_SELECT).hasValue('native-hcl'); - // TODO: Update to check for the actual formatted grant string. + assert.dom(selectors.FORMATTED_EXPORT_CODE_BLOCK).hasText(` + [ + "${instances.role.grant_strings[0]}", + "${instances.role.grant_strings[1]}", + ] + `); }); test('loads current grants in the editor and updates them', async function (assert) { @@ -183,4 +191,107 @@ module('Acceptance | roles/edit grants', function (hooks) { // is working as expected assert.dom(selectors.GRANT_ACTIONS_TABLE).isVisible(); }); + + test('shows "no type detected" in actions sidebar when role has no grants', async function (assert) { + const emptyRole = this.server.create('role', { + scope: instances.scopes.global, + grant_strings: [], + }); + const editGrantsUrl = `/scopes/global/roles/${emptyRole.id}/edit-grants`; + + await visit(editGrantsUrl); + + assert.dom(selectors.GRANT_ACTIONS_NO_TYPE_DETECTED).isVisible(); + assert.dom(selectors.GRANT_ACTIONS_TABLE).doesNotExist(); + }); + + test('actions sidebar updates to "no type detected" when cursor moves to an empty line', async function (assert) { + const role = this.server.create('role', { + scope: instances.scopes.global, + grant_strings: ['ids=*;type=role;actions=read', ''], + }); + const editGrantsUrl = `/scopes/global/roles/${role.id}/edit-grants`; + + await visit(editGrantsUrl); + await waitFor(commonSelectors.CODE_EDITOR_CM); + + // Line 1 is a valid grant — the actions table should be visible + assert.dom(selectors.GRANT_ACTIONS_TABLE).isVisible(); + + const editorElement = find(commonSelectors.CODE_EDITOR_CODE); + const editorView = editorElement.editor; + + // Move cursor to the second (empty) line + const line2 = editorView.state.doc.line(2); + editorView.dispatch({ selection: { anchor: line2.from } }); + + await waitFor(selectors.GRANT_ACTIONS_NO_TYPE_DETECTED); + + assert.dom(selectors.GRANT_ACTIONS_NO_TYPE_DETECTED).isVisible(); + assert.dom(selectors.GRANT_ACTIONS_TABLE).doesNotExist(); + }); + + test('actions sidebar shows "invalid id or type" for a grant with an unrecognised type', async function (assert) { + const role = this.server.create('role', { + scope: instances.scopes.global, + grant_strings: ['type=not-a-valid-type;actions=read'], + }); + const editGrantsUrl = `/scopes/global/roles/${role.id}/edit-grants`; + + await visit(editGrantsUrl); + await waitFor(commonSelectors.CODE_EDITOR_CM); + + assert.dom(selectors.GRANT_ACTIONS_INVALID_ID_OR_TYPE).isVisible(); + assert.dom(selectors.GRANT_ACTIONS_TABLE).doesNotExist(); + }); + + test('shows linting error markers for an invalid grant string', async function (assert) { + await visit(urls.editGrants); + await waitFor(commonSelectors.CODE_EDITOR_CM); + + const editorElement = find(commonSelectors.CODE_EDITOR_CODE); + const editorView = editorElement.editor; + + // Replace the editor content with an invalid grant string: + editorView.dispatch({ + changes: { + from: 0, + to: editorView.state.doc.length, + insert: 'ids=*', + }, + }); + + await waitFor(selectors.LINT_ERROR_MARKER); + + assert.dom(selectors.LINT_ERROR_MARKER).exists(); + }); + + test('shows autocomplete suggestions when triggered', async function (assert) { + await visit(urls.editGrants); + await waitFor(commonSelectors.CODE_EDITOR_CM); + + const editorElement = find(commonSelectors.CODE_EDITOR_CODE); + const editorView = editorElement.editor; + + // Insert a partial grant string ending with 'type=' so completions are expected + editorView.dispatch({ + changes: { + from: editorView.state.doc.length, + insert: '\ntype=', + }, + selection: { anchor: editorView.state.doc.length + '\ntype='.length }, + }); + + // Trigger Ctrl+Space + await triggerKeyEvent( + find(commonSelectors.CODE_EDITOR_CONTENT), + 'keydown', + ' ', + { ctrlKey: true }, + ); + + await waitFor(selectors.AUTOCOMPLETE_TOOLTIP); + + assert.dom(selectors.AUTOCOMPLETE_TOOLTIP).isVisible(); + }); }); diff --git a/ui/admin/tests/acceptance/roles/selectors.js b/ui/admin/tests/acceptance/roles/selectors.js index a7af3d228..b4d4d9942 100644 --- a/ui/admin/tests/acceptance/roles/selectors.js +++ b/ui/admin/tests/acceptance/roles/selectors.js @@ -57,6 +57,12 @@ export const REMOVE_ORG_PROJECTS_BTN = (name) => export const GRANT_ACTIONS_TABLE = '[data-test-grant-actions-table]'; export const GRANT_ACTIONS_NO_TYPE_DETECTED = '[data-test-grant-actions-no-type-detected]'; +export const GRANT_ACTIONS_INVALID_ID_OR_TYPE = + '[data-test-grant-actions-invalid-id-or-type]'; + +// Code mirror selectors +export const LINT_ERROR_MARKER = '.cm-lintRange-error'; +export const AUTOCOMPLETE_TOOLTIP = '.cm-tooltip-autocomplete'; // Role grants export const GRANT_CODE_BLOCK = '.grant-codeblock .hds-code-block__code';