diff --git a/changelog/unreleased/enhancement-enable-rename-groups b/changelog/unreleased/enhancement-enable-rename-groups new file mode 100644 index 00000000000..ce79f2abbf1 --- /dev/null +++ b/changelog/unreleased/enhancement-enable-rename-groups @@ -0,0 +1,6 @@ +Enhancement: Enable rename groups + +Groups can now be renamed via the admin-settings. + +https://github.com/owncloud/web/pull/8715 +https://github.com/owncloud/web/issues/8714 diff --git a/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue b/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue index de1e316ef4f..c346e777707 100644 --- a/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue +++ b/packages/web-app-admin-settings/src/components/Groups/ContextActions.vue @@ -9,7 +9,7 @@ import { useActionsShowDetails } from '../../../../web-pkg/src/composables/actio import { computed, defineComponent, PropType, unref } from 'vue' import ContextActionMenu from 'web-pkg/src/components/ContextActions/ContextActionMenu.vue' import { GroupActionOptions } from 'web-pkg/src/composables/actions' -import { useGroupActionsDelete } from '../../composables/actions/groups/useGroupActionsDelete' +import { useGroupActionsEdit, useGroupActionsDelete } from '../../composables/actions/groups' import { useStore } from 'web-pkg/src/composables' export default defineComponent({ @@ -25,9 +25,12 @@ export default defineComponent({ const store = useStore() const { actions: showDetailsActions } = useActionsShowDetails() const { actions: deleteActions } = useGroupActionsDelete({ store }) + const { actions: editActions } = useGroupActionsEdit() const menuItemsPrimaryActions = computed(() => - [...unref(deleteActions)].filter((item) => item.isEnabled(props.actionOptions)) + [...unref(editActions), ...unref(deleteActions)].filter((item) => + item.isEnabled(props.actionOptions) + ) ) const menuItemsSidebar = computed(() => diff --git a/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue b/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue index a787f99c67d..2fb6b82dc88 100644 --- a/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue +++ b/packages/web-app-admin-settings/src/components/Groups/CreateGroupModal.vue @@ -26,32 +26,28 @@ + diff --git a/packages/web-app-admin-settings/src/composables/actions/groups/index.ts b/packages/web-app-admin-settings/src/composables/actions/groups/index.ts index b8647746c40..307a19f2241 100644 --- a/packages/web-app-admin-settings/src/composables/actions/groups/index.ts +++ b/packages/web-app-admin-settings/src/composables/actions/groups/index.ts @@ -1 +1,2 @@ export * from './useGroupActionsDelete' +export * from './useGroupActionsEdit' diff --git a/packages/web-app-admin-settings/src/composables/actions/groups/useGroupActionsEdit.ts b/packages/web-app-admin-settings/src/composables/actions/groups/useGroupActionsEdit.ts new file mode 100644 index 00000000000..976cb1777a0 --- /dev/null +++ b/packages/web-app-admin-settings/src/composables/actions/groups/useGroupActionsEdit.ts @@ -0,0 +1,27 @@ +import { eventBus } from 'web-pkg' +import { SideBarEventTopics } from 'web-pkg/src/composables/sideBar' +import { useGettext } from 'vue3-gettext' +import { computed } from 'vue' +import { GroupAction } from 'web-pkg/src/composables/actions' + +export const useGroupActionsEdit = () => { + const { $gettext } = useGettext() + + const actions = computed((): GroupAction[] => [ + { + name: 'edit', + icon: 'pencil', + label: () => $gettext('Edit'), + handler: () => eventBus.publish(SideBarEventTopics.openWithPanel, 'EditPanel'), + isEnabled: ({ resources }) => { + return resources.length > 0 + }, + componentType: 'button', + class: 'oc-groups-actions-edit-trigger' + } + ]) + + return { + actions + } +} diff --git a/packages/web-app-admin-settings/src/views/Groups.vue b/packages/web-app-admin-settings/src/views/Groups.vue index 21035a4ade4..69ae4b1d5b5 100644 --- a/packages/web-app-admin-settings/src/views/Groups.vue +++ b/packages/web-app-admin-settings/src/views/Groups.vue @@ -63,7 +63,6 @@ @@ -186,10 +185,11 @@ export default defineComponent({ title: this.$gettext('Edit group'), component: EditPanel, default: false, - enabled: false // this.selectedGroups.length === 1 - /** - * Editing groups is currently not supported by backend - */ + enabled: this.selectedGroups.length === 1, + componentAttrs: { + group: this.selectedGroups.length === 1 ? this.selectedGroups[0] : null, + onConfirm: this.editGroup + } } ].filter((p) => p.enabled) } @@ -238,12 +238,20 @@ export default defineComponent({ try { const client = this.clientService.graphAuthenticated await client.groups.editGroup(editGroup.id, editGroup) - const group = this.groups.find((group) => group.id === editGroup.id) - Object.assign(group, editGroup) + const { data: updatedGroup } = await client.groups.getGroup(editGroup.id) + const groupIndex = this.groups.findIndex((group) => group.id === editGroup.id) + this.groups[groupIndex] = updatedGroup + const selectedGroupIndex = this.selectedGroups.findIndex( + (group) => group.id === updatedGroup.id + ) + if (selectedGroupIndex >= 0) { + // FIXME: why do we need to update selectedUsers? + this.selectedGroups[selectedGroupIndex] = updatedGroup + } - this.showMessage({ - title: this.$gettext('Group was edited successfully') - }) + eventBus.publish('sidebar.entity.saved') + + return updatedGroup } catch (error) { console.error(error) this.showMessage({ diff --git a/packages/web-app-admin-settings/tests/unit/components/Groups/ContextActions.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Groups/ContextActions.spec.ts index 4bf0bc2f5cf..5b9b6611c6e 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Groups/ContextActions.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Groups/ContextActions.spec.ts @@ -7,7 +7,10 @@ import { import { mock } from 'jest-mock-extended' import { Resource } from 'web-client/src/helpers' import ContextActions from '../../../../src/components/Groups/ContextActions.vue' -import { useGroupActionsDelete } from 'web-app-admin-settings/src/composables/actions' +import { + useGroupActionsDelete, + useGroupActionsEdit +} from 'web-app-admin-settings/src/composables/actions' import { computed } from 'vue' import { Action } from 'web-pkg/src/composables/actions' @@ -25,6 +28,12 @@ jest.mock('web-app-admin-settings/src/composables/actions/groups/useGroupActions ) ) +jest.mock('web-app-admin-settings/src/composables/actions/groups/useGroupActionsEdit', () => + createMockActionComposables( + jest.requireActual('web-app-admin-settings/src/composables/actions/groups/useGroupActionsEdit') + ) +) + const selectors = { actionMenuItemStub: 'action-menu-item-stub' } @@ -37,10 +46,13 @@ describe('ContextActions', () => { }) it('render enabled actions', () => { - const enabledComposables = [useGroupActionsDelete] + const enabledComposables = [useGroupActionsDelete, useGroupActionsEdit] jest.mocked(useGroupActionsDelete).mockImplementation(() => ({ actions: computed(() => [mock({ isEnabled: () => true })]) })) + jest.mocked(useGroupActionsEdit).mockImplementation(() => ({ + actions: computed(() => [mock({ isEnabled: () => true })]) + })) const { wrapper } = getWrapper() expect(wrapper.findAll(selectors.actionMenuItemStub).length).toBe(enabledComposables.length) }) diff --git a/packages/web-app-admin-settings/tests/unit/components/Groups/CreateGroupModal.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Groups/CreateGroupModal.spec.ts index 3057eebb4d7..6e5d1e9d65c 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Groups/CreateGroupModal.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Groups/CreateGroupModal.spec.ts @@ -1,5 +1,14 @@ import CreateGroupModal from '../../../../src/components/Groups/CreateGroupModal.vue' -import { defaultPlugins, shallowMount } from 'web-test-helpers' +import { + createStore, + defaultComponentMocks, + defaultPlugins, + defaultStoreMockOptions, + mockAxiosReject, + shallowMount +} from 'web-test-helpers' +import { mock } from 'jest-mock-extended' +import { AxiosResponse } from 'axios' describe('CreateGroupModal', () => { describe('computed method "isFormInvalid"', () => { @@ -8,48 +17,56 @@ describe('CreateGroupModal', () => { wrapper.vm.formData.displayName.valid = false expect(wrapper.vm.isFormInvalid).toBeTruthy() }) - }) - it('should be false if no data set is invalid', () => { - const { wrapper } = getWrapper() - Object.keys(wrapper.vm.formData).forEach((key) => { - wrapper.vm.formData[key].valid = true + it('should be false if no data set is invalid', () => { + const { wrapper } = getWrapper() + Object.keys(wrapper.vm.formData).forEach((key) => { + wrapper.vm.formData[key].valid = true + }) + expect(wrapper.vm.isFormInvalid).toBeFalsy() }) - expect(wrapper.vm.isFormInvalid).toBeFalsy() }) - describe('method "validateDisplayName"', () => { - it('should be false when displayName is empty', () => { + it('should be false when displayName is empty', async () => { const { wrapper } = getWrapper() wrapper.vm.group.displayName = '' - expect(wrapper.vm.validateDisplayName()).toBeFalsy() + expect(await wrapper.vm.validateDisplayName()).toBeFalsy() }) - it('should be false when displayName is already existing', () => { - const { wrapper } = getWrapper() + it('should be false when displayName is already existing', async () => { + const { wrapper, mocks } = getWrapper() + const graphMock = mocks.$clientService.graphAuthenticated wrapper.vm.group.displayName = 'admins' - expect(wrapper.vm.validateDisplayName()).toBeFalsy() + const getGroupSub = graphMock.groups.getGroup.mockResolvedValue( + mock({ data: { displayName: 'admins' } }) + ) + expect(await wrapper.vm.validateDisplayName()).toBeFalsy() + expect(getGroupSub).toHaveBeenCalled() }) - it('should be true when displayName is valid', () => { - const { wrapper } = getWrapper() + it('should be true when displayName is valid', async () => { + const { wrapper, mocks } = getWrapper() + const graphMock = mocks.$clientService.graphAuthenticated + const getGroupSub = graphMock.groups.getGroup.mockRejectedValue(() => mockAxiosReject()) wrapper.vm.group.displayName = 'users' - expect(wrapper.vm.validateDisplayName()).toBeTruthy() + expect(await wrapper.vm.validateDisplayName()).toBeTruthy() + expect(getGroupSub).toHaveBeenCalled() }) }) }) function getWrapper() { + const mocks = defaultComponentMocks() + const storeOptions = defaultStoreMockOptions + const store = createStore(storeOptions) + return { + mocks, wrapper: shallowMount(CreateGroupModal, { props: { cancel: jest.fn(), - confirm: jest.fn(), - existingGroups: [ - { - displayName: 'admins' - } - ] + confirm: jest.fn() }, global: { - plugins: [...defaultPlugins()] + mocks, + plugins: [...defaultPlugins(), store] } }) } diff --git a/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/DetailsPanel.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/DetailsPanel.spec.ts index bc3c4e6c698..492266cbf78 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/DetailsPanel.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/DetailsPanel.spec.ts @@ -4,8 +4,10 @@ import { defaultPlugins, mount } from 'web-test-helpers' describe('DetailsPanel', () => { describe('computed method "group"', () => { it('should be set if only one group is given', () => { - const { wrapper } = getWrapper({ propsData: { groups: [{ displayName: 'group' }] } }) - expect(wrapper.vm.group).toEqual({ displayName: 'group' }) + const { wrapper } = getWrapper({ + propsData: { groups: [{ displayName: 'group', members: [] }] } + }) + expect(wrapper.vm.group).toEqual({ displayName: 'group', members: [] }) }) it('should not be set if no groups are given', () => { const { wrapper } = getWrapper({ @@ -15,7 +17,12 @@ describe('DetailsPanel', () => { }) it('should not be set if multiple groups are given', () => { const { wrapper } = getWrapper({ - propsData: { groups: [{ displayName: 'group1' }, { displayName: 'group2' }] } + propsData: { + groups: [ + { displayName: 'group1', members: [] }, + { displayName: 'group2', members: [] } + ] + } }) expect(wrapper.vm.group).toEqual(null) }) @@ -29,7 +36,9 @@ describe('DetailsPanel', () => { expect(wrapper.vm.noGroups).toBeTruthy() }) it('should be false if groups are given', () => { - const { wrapper } = getWrapper({ propsData: { groups: [{ displayName: 'group' }] } }) + const { wrapper } = getWrapper({ + propsData: { groups: [{ displayName: 'group', members: [] }] } + }) expect(wrapper.vm.noGroups).toBeFalsy() }) }) @@ -40,12 +49,14 @@ describe('DetailsPanel', () => { expect(wrapper.vm.multipleGroups).toBeFalsy() }) it('should be false if one group is given', () => { - const { wrapper } = getWrapper({ propsData: { groups: [{ displayName: 'group' }] } }) + const { wrapper } = getWrapper({ + propsData: { groups: [{ displayName: 'group', members: [] }] } + }) expect(wrapper.vm.multipleGroups).toBeFalsy() }) it('should be true if multiple groups are given', () => { const { wrapper } = getWrapper({ - propsData: { groups: [{ displayName: 'group1' }, { displayName: 'group2' }] } + propsData: { groups: [{ displayName: 'group1' }, { displayName: 'group2', members: [] }] } }) expect(wrapper.vm.multipleGroups).toBeTruthy() }) diff --git a/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/EditPanel.spec.ts b/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/EditPanel.spec.ts index 0b1b3286a5a..922d86a21df 100644 --- a/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/EditPanel.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/EditPanel.spec.ts @@ -1,24 +1,29 @@ import EditPanel from '../../../../../src/components/Groups/SideBar/EditPanel.vue' -import { defaultPlugins, mount } from 'web-test-helpers' +import { + createStore, + defaultComponentMocks, + defaultPlugins, + defaultStoreMockOptions, + mockAxiosReject, + mount +} from 'web-test-helpers' +import { mock } from 'jest-mock-extended' +import { AxiosResponse } from 'axios' describe('EditPanel', () => { + it('renders all available inputs', () => { + const { wrapper } = getWrapper() + expect(wrapper.html()).toMatchSnapshot() + }) describe('method "revertChanges"', () => { it('should revert changes on property editGroup', () => { - const { wrapper } = getWrapper({ - propsData: { - groups: [{ displayName: 'group' }] - } - }) - wrapper.vm.editGroup = { displayName: 'my group' } + const { wrapper } = getWrapper() + wrapper.vm.editGroup.dispayName = 'users' wrapper.vm.revertChanges() - expect(wrapper.vm.editGroup).toEqual({ displayName: 'group' }) + expect(wrapper.vm.editGroup.displayName).toEqual('group') }) it('should revert changes on property formData', () => { - const { wrapper } = getWrapper({ - propsData: { - groups: [{ displayName: 'group' }] - } - }) + const { wrapper } = getWrapper() wrapper.vm.formData.displayName.valid = false wrapper.vm.formData.displayName.errorMessage = 'error' wrapper.vm.revertChanges() @@ -28,17 +33,28 @@ describe('EditPanel', () => { }) describe('method "validateDisplayName"', () => { - it('should return true if displayName is valid', () => { - const { wrapper } = getWrapper() - wrapper.vm.editGroup.displayName = 'jan' - expect(wrapper.vm.validateDisplayName()).toBeTruthy() - expect(wrapper.vm.formData.displayName.valid).toBeTruthy() + it('should return true if displayName is valid', async () => { + const { wrapper, mocks } = getWrapper() + wrapper.vm.editGroup.displayName = 'users' + const graphMock = mocks.$clientService.graphAuthenticated + const getGroupStub = graphMock.groups.getGroup.mockRejectedValue(() => mockAxiosReject()) + expect(await wrapper.vm.validateDisplayName()).toBeTruthy() + expect(getGroupStub).toHaveBeenCalled() }) - it('should return false if displayName is not valid', () => { + it('should return false if displayName is empty', async () => { const { wrapper } = getWrapper() wrapper.vm.editGroup.displayName = '' - expect(wrapper.vm.validateDisplayName()).toBeFalsy() - expect(wrapper.vm.formData.displayName.valid).toBeFalsy() + expect(await wrapper.vm.validateDisplayName()).toBeFalsy() + }) + it('should return false if displayName is already existing', async () => { + const { wrapper, mocks } = getWrapper() + wrapper.vm.editGroup.displayName = 'users' + const graphMock = mocks.$clientService.graphAuthenticated + const getGroupStub = graphMock.groups.getGroup.mockResolvedValue( + mock({ data: { displayName: 'group' } }) + ) + expect(await wrapper.vm.validateDisplayName()).toBeFalsy() + expect(getGroupStub).toHaveBeenCalled() }) }) @@ -56,15 +72,20 @@ describe('EditPanel', () => { }) }) -function getWrapper({ propsData = {} } = {}) { +function getWrapper() { + const mocks = defaultComponentMocks() + const storeOptions = defaultStoreMockOptions + const store = createStore(storeOptions) + return { + mocks, wrapper: mount(EditPanel, { props: { - groups: [{ displayName: 'group' }], - ...propsData + group: { displayName: 'group', members: [] } }, global: { - plugins: [...defaultPlugins()], + mocks, + plugins: [...defaultPlugins(), store], stubs: { 'oc-text-input': true, 'avatar-image': true, diff --git a/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/__snapshots__/EditPanel.spec.ts.snap b/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/__snapshots__/EditPanel.spec.ts.snap new file mode 100644 index 00000000000..94449e982f0 --- /dev/null +++ b/packages/web-app-admin-settings/tests/unit/components/Groups/SideBar/__snapshots__/EditPanel.spec.ts.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`EditPanel renders all available inputs 1`] = ` +
+
+ + group + 0 members +
+
+ +
+ No changes +
+ + +
+
+
+
+`; diff --git a/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts b/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts index e3b165ada12..bf21ae4d9da 100644 --- a/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts +++ b/packages/web-app-admin-settings/tests/unit/views/Groups.spec.ts @@ -1,7 +1,7 @@ import Groups from '../../../src/views/Groups.vue' import { mockAxiosResolve, mockAxiosReject } from 'web-test-helpers/src/mocks' import { mockDeep } from 'jest-mock-extended' -import { ClientService } from 'web-pkg/src' +import { ClientService, eventBus } from 'web-pkg/src' import { createStore, defaultComponentMocks, @@ -14,7 +14,7 @@ const selectors = { batchActionsStub: 'batch-actions-stub' } const getClientServiceMock = () => { const clientService = mockDeep() clientService.graphAuthenticated.groups.listGroups.mockImplementation(() => - mockAxiosResolve({ value: [{ id: '1' }] }) + mockAxiosResolve({ value: [{ id: '1', name: 'users' }] }) ) return clientService } @@ -49,12 +49,27 @@ describe('Groups view', () => { }) describe('method "editGroup"', () => { - it('should show message on success', async () => { - const { wrapper } = getWrapper() - const showMessageStub = jest.spyOn(wrapper.vm, 'showMessage') - await wrapper.vm.editGroup({ id: '1', displayName: 'Super group' }) + it('should emit event on success', async () => { + const clientService = getClientServiceMock() + clientService.graphAuthenticated.groups.editGroup.mockImplementation(() => mockAxiosResolve()) + clientService.graphAuthenticated.groups.getGroup.mockImplementation(() => + mockAxiosResolve({ id: '1', displayName: 'administrators' }) + ) + const { wrapper } = getWrapper({ clientService }) - expect(showMessageStub).toHaveBeenCalled() + const editGroup = { + id: '1', + name: 'administrators' + } + + const busStub = jest.spyOn(eventBus, 'publish') + await wrapper.vm.loadResourcesTask.last + + const updatedGroup = await wrapper.vm.editGroup(editGroup) + + expect(updatedGroup.id).toEqual('1') + expect(updatedGroup.displayName).toEqual('administrators') + expect(busStub).toHaveBeenCalled() }) it('should show message on error', async () => { @@ -70,11 +85,7 @@ describe('Groups view', () => { }) describe('computed method "sideBarAvailablePanels"', () => { - /** - * As soon as edit panel will be available in group management, please un-skip it. - */ - // eslint-disable-next-line jest/no-disabled-tests - it.skip('should contain EditPanel when one group is selected', () => { + it('should contain EditPanel when one group is selected', () => { const { wrapper } = getWrapper() wrapper.vm.selectedGroups = [{ id: '1' }] expect( diff --git a/packages/web-client/src/graph.ts b/packages/web-client/src/graph.ts index 90a11267eb8..c622cc95066 100644 --- a/packages/web-client/src/graph.ts +++ b/packages/web-client/src/graph.ts @@ -148,7 +148,8 @@ export const graph = (baseURI: string, axiosClient: AxiosInstance): Graph => { groups: { createGroup: (group: Group) => groupsApiFactory.createGroup(group), editGroup: (groupId: string, group: Group) => groupApiFactory.updateGroup(groupId, group), - getGroup: (groupId: string) => groupApiFactory.getGroup(groupId), + getGroup: (groupId: string) => + groupApiFactory.getGroup(groupId, new Set([]), new Set(['members'])), deleteGroup: (groupId: string) => groupApiFactory.deleteGroup(groupId), listGroups: (orderBy?: any) => groupsApiFactory.listGroups( diff --git a/tests/e2e/cucumber/features/smoke/admin-settings/groups.ocis.feature b/tests/e2e/cucumber/features/smoke/admin-settings/groups.ocis.feature index 6afa5f7d7f1..42689ea3dbe 100644 --- a/tests/e2e/cucumber/features/smoke/admin-settings/groups.ocis.feature +++ b/tests/e2e/cucumber/features/smoke/admin-settings/groups.ocis.feature @@ -39,3 +39,17 @@ Feature: groups management | security | | finance | And "Admin" logs out + + Scenario: edit groups + Given "Admin" creates following user using API + | id | + | Alice | + Given "Admin" creates following groups using API + | id | + | sales | + When "Admin" logs in + And "Admin" opens the "admin-settings" app + And "Admin" navigates to the groups management page + When "Admin" changes displayName to "a renamed group" for group "sales" using the sidebar panel + And "Admin" logs out + diff --git a/tests/e2e/cucumber/steps/ui/adminSettings.ts b/tests/e2e/cucumber/steps/ui/adminSettings.ts index 502b57852fd..14ea6db813c 100644 --- a/tests/e2e/cucumber/steps/ui/adminSettings.ts +++ b/tests/e2e/cucumber/steps/ui/adminSettings.ts @@ -428,6 +428,27 @@ When( } ) +When( + /^"([^"]*)" changes (displayName) to "([^"]*)" for group "([^"]*)" using the sidebar panel$/, + async function ( + this: World, + stepUser: string, + attribute: string, + value: string, + user: string + ): Promise { + const { page } = this.actorsEnvironment.getActor({ key: stepUser }) + const groupsObject = new objects.applicationAdminSettings.Groups({ page }) + + await groupsObject.changeGroup({ + key: user, + attribute: attribute, + value: value, + action: 'context-menu' + }) + } +) + Then( /^"([^"]*)" (should see|should not see) the following group(?:s)?$/, async function ( diff --git a/tests/e2e/support/objects/app-admin-settings/groups/actions.ts b/tests/e2e/support/objects/app-admin-settings/groups/actions.ts index e4609c058c3..eb7804c5421 100644 --- a/tests/e2e/support/objects/app-admin-settings/groups/actions.ts +++ b/tests/e2e/support/objects/app-admin-settings/groups/actions.ts @@ -1,14 +1,22 @@ import { Page } from 'playwright' import util from 'util' +import { selectUser } from '../users/actions' const newGroupBtn = '.admin-settings-app-bar-actions' const createGroupInput = '#create-group-input-display-name' const actionConfirmButton = '.oc-modal-body-actions-confirm' +const editActionBtnContextMenu = '.context-menu .oc-groups-actions-edit-trigger' +const editActionBtnQuickActions = + '[data-item-id="%s"] .oc-table-data-cell-actions .groups-table-btn-edit' const groupTrSelector = 'tr' const groupIdSelector = `[data-item-id="%s"] .groups-table-btn-action-dropdown` const groupCheckboxSelector = `[data-item-id="%s"]:not(.oc-table-highlighted) input[type=checkbox]` const deleteBtnContextMenu = '.context-menu .oc-groups-actions-delete-trigger' const deleteBtnBatchAction = '#oc-appbar-batch-actions' +const editPanel = '.sidebar-panel__body-EditPanel:visible' +const closeEditPanel = '.sidebar-panel__header .header__close' +const userInput = '#%s-input' +const compareDialogConfirm = '.compare-save-dialog-confirm-btn' export const createGroup = async (args: { page: Page; key: string }): Promise => { const { page, key } = args @@ -89,3 +97,45 @@ export const deleteGrouprUsingBatchAction = async (args: { await Promise.all([...checkResponses, page.locator(actionConfirmButton).click()]) } + +export const changeGroup = async (args: { + page: Page + uuid: string + attribute: string + value: string +}): Promise => { + const { page, attribute, value, uuid } = args + await page.locator(util.format(userInput, attribute)).fill(value) + + await Promise.all([ + page.waitForResponse( + (resp) => + resp.url().endsWith(encodeURIComponent(uuid)) && + resp.status() === 204 && + resp.request().method() === 'PATCH' + ), + await page.locator(compareDialogConfirm).click() + ]) +} +export const openEditPanel = async (args: { + page: Page + uuid: string + action: string +}): Promise => { + const { page, uuid, action } = args + if (await page.locator(editPanel).count()) { + await page.locator(closeEditPanel).click() + } + switch (action) { + case 'context-menu': + await page.locator(util.format(groupIdSelector, uuid)).click() + await page.locator(editActionBtnContextMenu).click() + break + case 'quick-action': + await selectUser({ page, uuid }) + await page.locator(util.format(editActionBtnQuickActions, uuid)).click() + break + default: + throw new Error(`${action} not implemented`) + } +} diff --git a/tests/e2e/support/objects/app-admin-settings/groups/index.ts b/tests/e2e/support/objects/app-admin-settings/groups/index.ts index 578a6b26e80..152962c39f2 100644 --- a/tests/e2e/support/objects/app-admin-settings/groups/index.ts +++ b/tests/e2e/support/objects/app-admin-settings/groups/index.ts @@ -2,10 +2,12 @@ import { Page } from 'playwright' import { UsersEnvironment } from '../../../environment' import { createGroup, + openEditPanel, getDisplayedGroups, selectGroup, deleteGroupUsingContextMenu, - deleteGrouprUsingBatchAction + deleteGrouprUsingBatchAction, + changeGroup } from './actions' export class Groups { @@ -33,4 +35,24 @@ export class Groups { async deleteGroupUsingContextMenu({ key }: { key: string }): Promise { await deleteGroupUsingContextMenu({ page: this.#page, uuid: this.getUUID({ key }) }) } + + async changeGroup({ + key, + attribute, + value, + action + }: { + key: string + attribute: string + value: string + action: string + }): Promise { + const uuid = this.getUUID({ key }) + await openEditPanel({ page: this.#page, uuid, action }) + await changeGroup({ uuid, attribute: attribute, value: value, page: this.#page }) + } + + async openEditPanel({ key, action }: { key: string; action: string }): Promise { + await openEditPanel({ page: this.#page, uuid: this.getUUID({ key }), action }) + } }