From bcedf5c76f669b424b2d124dd658c29e99da9174 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Thu, 18 Sep 2025 17:15:05 +0200 Subject: [PATCH] fix(editor): Reintroduce user deletion actions in the members table in Users and Project settings page (#19604) --- cypress/pages/settings-users.ts | 5 +- .../ProjectMembersActionsCell.test.ts | 86 ++++++++++++ .../Projects/ProjectMembersActionsCell.vue | 27 ++++ .../Projects/ProjectMembersRoleCell.test.ts | 36 +---- .../Projects/ProjectMembersRoleCell.vue | 15 +- .../Projects/ProjectMembersTable.test.ts | 13 ++ .../Projects/ProjectMembersTable.vue | 47 ++++--- .../SettingsUsersActionsCell.test.ts | 6 - .../SettingsUsersActionsCell.vue | 2 +- .../SettingsUsersRoleCell.test.ts | 13 +- .../SettingsUsers/SettingsUsersRoleCell.vue | 10 +- .../SettingsUsers/SettingsUsersTable.test.ts | 12 -- .../SettingsUsers/SettingsUsersTable.vue | 13 +- .../src/stores/projects.store.test.ts | 127 +++++++++++++++++ .../editor-ui/src/stores/projects.store.ts | 23 ++- .../src/views/ProjectSettings.test.ts | 21 +-- .../editor-ui/src/views/ProjectSettings.vue | 132 +++++++++++------- .../src/views/SettingsUsersView.test.ts | 33 ++++- .../editor-ui/src/views/SettingsUsersView.vue | 7 + packages/testing/playwright/pages/BasePage.ts | 17 +++ .../playwright/pages/ProjectSettingsPage.ts | 9 +- .../playwright/tests/ui/39-projects.spec.ts | 20 +-- 22 files changed, 483 insertions(+), 191 deletions(-) create mode 100644 packages/frontend/editor-ui/src/components/Projects/ProjectMembersActionsCell.test.ts create mode 100644 packages/frontend/editor-ui/src/components/Projects/ProjectMembersActionsCell.vue create mode 100644 packages/frontend/editor-ui/src/stores/projects.store.test.ts diff --git a/cypress/pages/settings-users.ts b/cypress/pages/settings-users.ts index 00cc6ff837..4f3b33a45c 100644 --- a/cypress/pages/settings-users.ts +++ b/cypress/pages/settings-users.ts @@ -3,7 +3,6 @@ import { MainSidebar } from './sidebar/main-sidebar'; import { SettingsSidebar } from './sidebar/settings-sidebar'; import { WorkflowPage } from './workflow'; import { WorkflowsPage } from './workflows'; -import { getVisiblePopper } from '../utils'; const workflowPage = new WorkflowPage(); const workflowsPage = new WorkflowsPage(); @@ -62,8 +61,8 @@ export class SettingsUsersPage extends BasePage { } }, opedDeleteDialog: (email: string) => { - this.getters.userRoleSelect(email).find('button').should('be.visible').click(); - getVisiblePopper().find('span').contains('Remove user').click(); + this.getters.userActionsToggle(email).should('be.visible').click(); + this.getters.deleteUserAction().click(); this.getters.confirmDeleteModal().should('be.visible'); }, }; diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectMembersActionsCell.test.ts b/packages/frontend/editor-ui/src/components/Projects/ProjectMembersActionsCell.test.ts new file mode 100644 index 0000000000..16a38ef2e9 --- /dev/null +++ b/packages/frontend/editor-ui/src/components/Projects/ProjectMembersActionsCell.test.ts @@ -0,0 +1,86 @@ +import { createTestingPinia } from '@pinia/testing'; +import { screen } from '@testing-library/vue'; +import userEvent from '@testing-library/user-event'; +import { vi } from 'vitest'; +import type { UserAction } from '@n8n/design-system'; +import ProjectMembersActionsCell from '@/components/Projects/ProjectMembersActionsCell.vue'; +import { createComponentRenderer } from '@/__tests__/render'; +import type { ProjectMemberData } from '@/types/projects.types'; + +vi.mock('@n8n/design-system', async (importOriginal) => { + const original = await importOriginal(); + return { + ...original, + N8nActionToggle: { + name: 'N8nActionToggle', + props: { + actions: { type: Array, required: true }, + }, + emits: ['action'], + template: ` +
+ +
+ `, + }, + }; +}); + +const baseMember: ProjectMemberData = { + id: '1', + firstName: 'John', + lastName: 'Doe', + email: 'john@example.com', + role: 'project:editor', +}; + +const removeAction: UserAction = { + value: 'remove', + label: 'Remove user', +}; + +let renderComponent: ReturnType; + +describe('ProjectMembersActionsCell', () => { + beforeEach(() => { + renderComponent = createComponentRenderer(ProjectMembersActionsCell, { + pinia: createTestingPinia(), + }); + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('renders actions when allowed and emits on click', async () => { + const props = { + data: baseMember, + actions: [removeAction], + }; + const { emitted } = renderComponent({ props }); + + const user = userEvent.setup(); + await user.click(screen.getByTestId('action-remove')); + + expect(emitted()).toHaveProperty('action'); + expect(emitted().action[0]).toEqual([{ action: 'remove', userId: '1' }]); + }); + + it('does not render when actions list is empty', () => { + const props = { + data: baseMember, + actions: [], + }; + const { container } = renderComponent({ props }); + expect(container.querySelector('button')).toBeNull(); + }); + + // Visibility filtering is handled by ProjectMembersTable now +}); diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectMembersActionsCell.vue b/packages/frontend/editor-ui/src/components/Projects/ProjectMembersActionsCell.vue new file mode 100644 index 0000000000..0dd32e13e1 --- /dev/null +++ b/packages/frontend/editor-ui/src/components/Projects/ProjectMembersActionsCell.vue @@ -0,0 +1,27 @@ + + + diff --git a/packages/frontend/editor-ui/src/components/Projects/ProjectMembersRoleCell.test.ts b/packages/frontend/editor-ui/src/components/Projects/ProjectMembersRoleCell.test.ts index f581ce978c..bf1880a62a 100644 --- a/packages/frontend/editor-ui/src/components/Projects/ProjectMembersRoleCell.test.ts +++ b/packages/frontend/editor-ui/src/components/Projects/ProjectMembersRoleCell.test.ts @@ -26,8 +26,8 @@ vi.mock('@n8n/design-system', async (importOriginal) => {
  • - + diff --git a/packages/frontend/editor-ui/src/components/SettingsUsers/SettingsUsersActionsCell.test.ts b/packages/frontend/editor-ui/src/components/SettingsUsers/SettingsUsersActionsCell.test.ts index 9b7918a43a..926d98fa9a 100644 --- a/packages/frontend/editor-ui/src/components/SettingsUsers/SettingsUsersActionsCell.test.ts +++ b/packages/frontend/editor-ui/src/components/SettingsUsers/SettingsUsersActionsCell.test.ts @@ -38,12 +38,6 @@ describe('SettingsUsersActionsCell', () => { vi.clearAllMocks(); }); - it('should not render action toggle for an owner', () => { - const props = { data: { ...baseUser, isOwner: true }, actions: mockActions }; - const { container } = renderComponent({ props }); - expect(container.firstChild).toBeEmptyDOMElement(); - }); - it('should not render action toggle if there are no actions', () => { const props = { data: baseUser, actions: [] }; const { container } = renderComponent({ props }); diff --git a/packages/frontend/editor-ui/src/components/SettingsUsers/SettingsUsersActionsCell.vue b/packages/frontend/editor-ui/src/components/SettingsUsers/SettingsUsersActionsCell.vue index 373d425b3b..dbad53e945 100644 --- a/packages/frontend/editor-ui/src/components/SettingsUsers/SettingsUsersActionsCell.vue +++ b/packages/frontend/editor-ui/src/components/SettingsUsers/SettingsUsersActionsCell.vue @@ -23,7 +23,7 @@ const onUserAction = (action: string) => {