feat(editor): Combine 'Move to Folder' and 'Change owner' modals (#15756)

This new modal also allows transferring entire folders to other projects and users.
This commit is contained in:
Jaakko Husso
2025-05-28 23:41:07 +03:00
committed by GitHub
parent ba70cab9d5
commit e860dd6d2e
27 changed files with 1989 additions and 292 deletions

View File

@@ -206,6 +206,14 @@ export function getMoveToFolderInput() {
return getMoveToFolderDropdown().find('input');
}
export function getProjectSharingInput() {
return cy.getByTestId('project-sharing-select');
}
export function getProjectSharingOption(name: string) {
return cy.getByTestId('project-sharing-info').contains(name);
}
export function getEmptyFolderDropdownMessage(text: string) {
return cy.get('.el-select-dropdown__empty').contains(text);
}
@@ -500,7 +508,12 @@ function deleteFolderAndMoveContents(folderName: string, destinationName: string
function moveFolder(folderName: string, destinationName: string) {
cy.intercept('PATCH', '/rest/projects/**').as('moveFolder');
getMoveFolderModal().should('be.visible');
getMoveFolderModal().find('h1').first().contains(`Move "${folderName}" to another folder`);
getMoveFolderModal().find('h1').first().contains(`Move folder ${folderName}`);
// The dropdown focuses after a small delay (once modal's slide in animation is done).
// On the component we listen for an event, but here the wait should be very predictable.
cy.wait(500);
// Try to find current folder in the dropdown
// This tests that auto-focus worked as expected
cy.focused().type(folderName, { delay: 50 });
@@ -514,3 +527,27 @@ function moveFolder(folderName: string, destinationName: string) {
getMoveFolderConfirmButton().should('be.enabled').click();
cy.wait('@moveFolder');
}
export function transferWorkflow(
workflowName: string,
projectName: string,
destinationFolder?: string,
) {
getMoveFolderModal().should('be.visible');
getMoveFolderModal().find('h1').first().contains(`Move workflow ${workflowName}`);
cy.wait(500);
getProjectSharingInput().should('be.visible').click();
cy.focused().type(projectName, { delay: 50 });
getProjectSharingOption(projectName).should('be.visible').click();
if (destinationFolder) {
getMoveToFolderInput().click();
// Select destination folder
cy.focused().type(destinationFolder, { delay: 50 });
getMoveToFolderOption(destinationFolder).should('be.visible').click();
}
getMoveFolderConfirmButton().should('be.enabled').click();
}

View File

@@ -5,6 +5,7 @@ const workflowPage = new WorkflowPage();
const credentialsModal = new CredentialsModal();
export const getHomeButton = () => cy.getByTestId('project-home-menu-item');
export const getPersonalProjectsButton = () => cy.getByTestId('project-personal-menu-item');
export const getMenuItems = () => cy.getByTestId('project-menu-item');
export const getAddProjectButton = () => {
cy.getByTestId('universal-add').should('be.visible').click();
@@ -62,6 +63,8 @@ export const addProjectMember = (email: string, role?: string) => {
};
export const getResourceMoveModal = () => cy.getByTestId('project-move-resource-modal');
export const getProjectMoveSelect = () => cy.getByTestId('project-move-resource-modal-select');
export const getProjectSharingSelect = () => cy.getByTestId('project-sharing-select');
export const getMoveToFolderSelect = () => cy.getByTestId('move-to-folder-dropdown');
export function createProject(name: string) {
getAddProjectButton().click();

View File

@@ -1,4 +1,5 @@
import { createResource } from '../composables/create';
import { transferWorkflow } from '../composables/folders';
import { setCredentialValues } from '../composables/modals/credential-modal';
import {
clickCreateNewCredential,
@@ -37,6 +38,7 @@ describe('Projects', { disableAutoLogin: true }, () => {
beforeEach(() => {
cy.resetDatabase();
cy.enableFeature('sharing');
cy.enableFeature('folders');
cy.enableFeature('advancedPermissions');
cy.enableFeature('projectRole:admin');
cy.enableFeature('projectRole:editor');
@@ -246,21 +248,14 @@ describe('Projects', { disableAutoLogin: true }, () => {
projects.getProjectSettingsSaveButton().click();
// Move the workflow from Home to Project 1
projects.getHomeButton().click();
projects.getPersonalProjectsButton().click();
workflowsPage.getters.workflowCards().should('have.length', 1);
workflowsPage.getters.workflowCards().filter(':contains("Personal")').should('exist');
workflowsPage.getters.workflowCardActions('My workflow').click();
workflowsPage.getters.workflowMoveButton().click();
projects
.getResourceMoveModal()
.should('be.visible')
.contains('button', 'Move workflow')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect().find('li').should('have.length', 4);
getVisibleSelect().find('li').filter(':contains("Project 1")').click();
projects.getResourceMoveModal().contains('button', 'Move workflow').click();
transferWorkflow('My workflow', 'Project 1', 'No folder (project root)');
projects.getMenuItems().filter(':contains("Project 1")').click();
clearNotifications();
cy.wait('@getResources');
@@ -396,6 +391,7 @@ describe('Projects', { disableAutoLogin: true }, () => {
before(() => {
cy.resetDatabase();
cy.enableFeature('sharing');
cy.enableFeature('folders');
cy.enableFeature('advancedPermissions');
cy.enableFeature('projectRole:admin');
cy.enableFeature('projectRole:editor');
@@ -453,93 +449,35 @@ describe('Projects', { disableAutoLogin: true }, () => {
cy.signinAsOwner();
cy.visit(workflowsPage.url);
projects.getHomeButton().click();
workflowsPage.getters.workflowCards().should('have.length', 3);
workflowsPage.getters.workflowCards().filter(':contains("Personal")').should('exist');
projects.getPersonalProjectsButton().click();
workflowsPage.getters.workflowCards().should('have.length', 1);
workflowsPage.getters.workflowCardActions('Workflow in Home project').click();
workflowsPage.getters.workflowMoveButton().click();
projects
.getResourceMoveModal()
.should('be.visible')
.contains('button', 'Move workflow')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect().find('li').should('have.length', 5);
getVisibleSelect().find('li').filter(':contains("Project 1")').click();
projects.getResourceMoveModal().contains('button', 'Move workflow').click();
transferWorkflow('Workflow in Home project', 'Project 2', 'No folder (project root)');
clearNotifications();
cy.wait('@getResources');
workflowsPage.getters.workflowCards().should('have.length', 3);
workflowsPage.getters.workflowCards().filter(':contains("Personal")').should('not.exist');
workflowsPage.getters.workflowCards().should('have.length', 0);
cy.log('Move the workflow from Project 1 to Project 2');
projects.getMenuItems().first().click();
workflowsPage.getters.workflowCards().should('have.length', 2);
workflowsPage.getters.workflowCardActions('Workflow in Home project').click();
workflowsPage.getters.workflowCards().should('have.length', 1);
workflowsPage.getters.workflowCardActions('Workflow in Project 1').click();
workflowsPage.getters.workflowMoveButton().click();
projects
.getResourceMoveModal()
.should('be.visible')
.contains('button', 'Move workflow')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect().find('li').should('have.length', 5);
getVisibleSelect().find('li').filter(':contains("Project 2")').click();
projects.getResourceMoveModal().contains('button', 'Move workflow').click();
transferWorkflow('Workflow in Project 1', 'Project 2', 'No folder (project root)');
clearNotifications();
cy.log('Move the workflow from Project 2 to a member user');
projects.getMenuItems().last().click();
workflowsPage.getters.workflowCards().should('have.length', 3);
workflowsPage.getters.workflowCardActions('Workflow in Home project').click();
workflowsPage.getters.workflowMoveButton().click();
transferWorkflow('Workflow in Home project', INSTANCE_MEMBERS[0].email);
clearNotifications();
workflowsPage.getters.workflowCards().should('have.length', 2);
workflowsPage.getters.workflowCardActions('Workflow in Home project').click();
workflowsPage.getters.workflowMoveButton().click();
projects
.getResourceMoveModal()
.should('be.visible')
.contains('button', 'Move workflow')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect().find('li').should('have.length', 5);
getVisibleSelect().find('li').filter(`:contains("${INSTANCE_MEMBERS[0].email}")`).click();
projects.getResourceMoveModal().contains('button', 'Move workflow').click();
clearNotifications();
cy.wait('@getResources');
workflowsPage.getters.workflowCards().should('have.length', 1);
cy.log('Move the workflow from member user back to Home');
projects.getHomeButton().click();
workflowsPage.getters.workflowCards().should('have.length', 3);
workflowsPage.getters
.workflowCards()
.filter(':has([data-test-id="card-badge"]:contains("Project"))')
.should('have.length', 2);
workflowsPage.getters.workflowCardActions('Workflow in Home project').click();
workflowsPage.getters.workflowMoveButton().click();
projects
.getResourceMoveModal()
.should('be.visible')
.contains('button', 'Move workflow')
.should('be.disabled');
projects.getProjectMoveSelect().click();
getVisibleSelect().find('li').should('have.length', 5);
getVisibleSelect().find('li').filter(`:contains("${INSTANCE_OWNER.email}")`).click();
projects.getResourceMoveModal().contains('button', 'Move workflow').click();
clearNotifications();
cy.wait('@getResources');
workflowsPage.getters.workflowCards().should('have.length', 3);
workflowsPage.getters
.workflowCards()
.filter(':contains("Personal")')
.should('have.length', 1);
});
it('should move the credential to expected projects', () => {

View File

@@ -465,7 +465,7 @@ describe('Folders', () => {
goToPersonalProject();
createFolderFromProjectHeader('Test parent');
createFolderInsideFolder('Move me to root', 'Test parent');
moveFolderFromFolderCardActions('Move me to root', 'Personal');
moveFolderFromFolderCardActions('Move me to root', 'No folder (project root)');
// Parent folder should be empty
getFolderEmptyState().should('exist');
// Child folder should be in the root

View File

@@ -43,7 +43,7 @@ export class WorkflowsPage extends BasePage {
workflowDeleteButton: () =>
cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Delete'),
workflowMoveButton: () =>
cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Change owner'),
cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Move'),
workflowFilterButton: () => cy.getByTestId('resources-list-filters-trigger').filter(':visible'),
workflowTagsDropdown: () => cy.getByTestId('tags-dropdown'),
workflowTagItem: (tag: string) => cy.getByTestId('tag').contains(tag),