diff --git a/cypress/e2e/17-sharing.cy.ts b/cypress/e2e/17-sharing.cy.ts index 4ce7e5701f..51b4a674d3 100644 --- a/cypress/e2e/17-sharing.cy.ts +++ b/cypress/e2e/17-sharing.cy.ts @@ -259,7 +259,7 @@ describe('Sharing', { disableAutoLogin: true }, () => { credentialsPage.getters .credentialCards() .should('have.length', 2) - .filter(':contains("Owned by me")') + .filter(':contains("Personal")') .should('have.length', 1); }); }); diff --git a/cypress/e2e/39-projects.cy.ts b/cypress/e2e/39-projects.cy.ts index 5ea9b99e27..138f67838a 100644 --- a/cypress/e2e/39-projects.cy.ts +++ b/cypress/e2e/39-projects.cy.ts @@ -15,12 +15,8 @@ import { NDV, MainSidebar, } from '../pages'; -import { - getVisibleDropdown, - getVisibleModalOverlay, - getVisibleSelect, - getVisiblePopper, -} from '../utils'; +import { clearNotifications } from '../pages/notifications'; +import { getVisibleDropdown, getVisibleModalOverlay, getVisibleSelect } from '../utils'; const workflowsPage = new WorkflowsPage(); const workflowPage = new WorkflowPage(); @@ -453,38 +449,48 @@ describe('Projects', { disableAutoLogin: true }, () => { workflowsPage.getters.workflowCards().should('not.have.length'); workflowsPage.getters.newWorkflowButtonCard().click(); projects.createWorkflow('Test_workflow_1.json', 'Workflow in Home project'); + clearNotifications(); projects.getHomeButton().click(); projects.getProjectTabCredentials().should('be.visible').click(); credentialsPage.getters.emptyListCreateCredentialButton().click(); projects.createCredential('Credential in Home project'); + clearNotifications(); + // Create a project and add a credential and a workflow to it projects.createProject('Project 1'); + clearNotifications(); projects.getProjectTabCredentials().click(); credentialsPage.getters.emptyListCreateCredentialButton().click(); projects.createCredential('Credential in Project 1'); + clearNotifications(); projects.getProjectTabWorkflows().click(); workflowsPage.getters.newWorkflowButtonCard().click(); projects.createWorkflow('Test_workflow_1.json', 'Workflow in Project 1'); + clearNotifications(); + // Create another project and add a credential and a workflow to it projects.createProject('Project 2'); + clearNotifications(); projects.getProjectTabCredentials().click(); credentialsPage.getters.emptyListCreateCredentialButton().click(); projects.createCredential('Credential in Project 2'); + clearNotifications(); projects.getProjectTabWorkflows().click(); workflowsPage.getters.newWorkflowButtonCard().click(); projects.createWorkflow('Test_workflow_1.json', 'Workflow in Project 2'); + clearNotifications(); - // Move the workflow owned by me from Home to Project 1 + // Move the workflow Personal from Home to Project 1 projects.getHomeButton().click(); workflowsPage.getters .workflowCards() .should('have.length', 3) - .filter(':contains("Owned by me")') + .filter(':contains("Personal")') .should('exist'); workflowsPage.getters.workflowCardActions('Workflow in Home project').click(); workflowsPage.getters.workflowMoveButton().click(); @@ -501,11 +507,12 @@ describe('Projects', { disableAutoLogin: true }, () => { .filter(':contains("Project 1")') .click(); projects.getResourceMoveModal().find('button:contains("Move workflow")').click(); + clearNotifications(); workflowsPage.getters .workflowCards() .should('have.length', 3) - .filter(':contains("Owned by me")') + .filter(':contains("Personal")') .should('not.exist'); // Move the workflow from Project 1 to Project 2 @@ -532,6 +539,7 @@ describe('Projects', { disableAutoLogin: true }, () => { workflowsPage.getters.workflowCards().should('have.length', 2); workflowsPage.getters.workflowCardActions('Workflow in Home project').click(); workflowsPage.getters.workflowMoveButton().click(); + clearNotifications(); projects .getResourceMoveModal() @@ -571,10 +579,11 @@ describe('Projects', { disableAutoLogin: true }, () => { .click(); projects.getResourceMoveModal().find('button:contains("Move workflow")').click(); + clearNotifications(); workflowsPage.getters .workflowCards() .should('have.length', 3) - .filter(':contains("Owned by me")') + .filter(':contains("Personal")') .should('have.length', 1); // Move the credential from Project 1 to Project 2 @@ -584,9 +593,6 @@ describe('Projects', { disableAutoLogin: true }, () => { credentialsPage.getters.credentialCardActions('Credential in Project 1').click(); credentialsPage.getters.credentialMoveButton().click(); - // wait for all poppers to be gone - getVisiblePopper().should('have.length', 0); - projects .getResourceMoveModal() .should('be.visible') @@ -599,7 +605,7 @@ describe('Projects', { disableAutoLogin: true }, () => { .filter(':contains("Project 2")') .click(); projects.getResourceMoveModal().find('button:contains("Move credential")').click(); - + clearNotifications(); credentialsPage.getters.credentialCards().should('not.have.length'); // Move the credential from Project 2 to admin user @@ -610,9 +616,6 @@ describe('Projects', { disableAutoLogin: true }, () => { credentialsPage.getters.credentialCardActions('Credential in Project 1').click(); credentialsPage.getters.credentialMoveButton().click(); - // wait for all poppers to be gone - getVisiblePopper().should('have.length', 0); - projects .getResourceMoveModal() .should('be.visible') @@ -635,9 +638,6 @@ describe('Projects', { disableAutoLogin: true }, () => { credentialsPage.getters.credentialCardActions('Credential in Project 1').click(); credentialsPage.getters.credentialMoveButton().click(); - // wait for all poppers to be gone - getVisiblePopper().should('have.length', 0); - projects .getResourceMoveModal() .should('be.visible') @@ -651,13 +651,12 @@ describe('Projects', { disableAutoLogin: true }, () => { .click(); projects.getResourceMoveModal().find('button:contains("Move credential")').click(); - // wait for all poppers to be gone - getVisiblePopper().should('have.length', 0); + clearNotifications(); credentialsPage.getters .credentialCards() .should('have.length', 3) - .filter(':contains("Owned by me")') + .filter(':contains("Personal")') .should('have.length', 2); // Move the credential from admin user back to its original project (Project 1) @@ -716,7 +715,7 @@ describe('Projects', { disableAutoLogin: true }, () => { workflowsPage.getters .workflowCards() .should('have.length', 1) - .filter(':contains("Owned by me")') + .filter(':contains("Personal")') .should('exist'); workflowsPage.getters.workflowCardActions('My workflow').click(); workflowsPage.getters.workflowMoveButton().click(); @@ -737,7 +736,7 @@ describe('Projects', { disableAutoLogin: true }, () => { workflowsPage.getters .workflowCards() .should('have.length', 1) - .filter(':contains("Owned by me")') + .filter(':contains("Personal")') .should('not.exist'); //Log out with instance owner and log in with the member user diff --git a/packages/editor-ui/src/components/Projects/ProjectCardBadge.test.ts b/packages/editor-ui/src/components/Projects/ProjectCardBadge.test.ts index add92bd45c..3ac8e3d5a8 100644 --- a/packages/editor-ui/src/components/Projects/ProjectCardBadge.test.ts +++ b/packages/editor-ui/src/components/Projects/ProjectCardBadge.test.ts @@ -5,7 +5,7 @@ import { truncate } from 'n8n-design-system'; const renderComponent = createComponentRenderer(ProjectCardBadge); describe('ProjectCardBadge', () => { - it('should show "Owned by me" badge if there is no homeProject', () => { + it('should show "Personal" badge if there is no homeProject', () => { const { getByText } = renderComponent({ props: { resource: {}, @@ -13,15 +13,16 @@ describe('ProjectCardBadge', () => { }, }); - expect(getByText('Owned by me')).toBeVisible(); + expect(getByText('Personal')).toBeVisible(); }); - it('should show "Owned by me" badge if homeProject ID equals personalProject ID', () => { + it('should show "Personal" badge if homeProject ID equals personalProject ID', () => { const { getByText } = renderComponent({ props: { resource: { homeProject: { id: '1', + name: 'John', }, }, resourceType: 'workflow', @@ -31,7 +32,27 @@ describe('ProjectCardBadge', () => { }, }); - expect(getByText('Owned by me')).toBeVisible(); + expect(getByText('Personal')).toBeVisible(); + }); + + it('should show shared with count', () => { + const { getByText } = renderComponent({ + props: { + resource: { + sharedWithProjects: [{}, {}, {}], + homeProject: { + id: '1', + name: 'John', + }, + }, + resourceType: 'workflow', + personalProject: { + id: '1', + }, + }, + }); + + expect(getByText('+ 3')).toBeVisible(); }); test.each([ diff --git a/packages/editor-ui/src/components/Projects/ProjectCardBadge.vue b/packages/editor-ui/src/components/Projects/ProjectCardBadge.vue index ef675aa232..ce3a238c37 100644 --- a/packages/editor-ui/src/components/Projects/ProjectCardBadge.vue +++ b/packages/editor-ui/src/components/Projects/ProjectCardBadge.vue @@ -52,12 +52,17 @@ const projectState = computed(() => { } return ProjectState.Unknown; }); + +const numberOfMembersInHomeTeamProject = computed( + () => props.resource.sharedWithProjects?.length ?? 0, +); + const badgeText = computed(() => { if ( projectState.value === ProjectState.Owned || projectState.value === ProjectState.SharedOwned ) { - return i18n.baseText('generic.ownedByMe'); + return i18n.baseText('projects.menu.personal'); } else { const { name, email } = splitName(props.resource.homeProject?.name ?? ''); return name ?? email ?? ''; @@ -65,12 +70,12 @@ const badgeText = computed(() => { }); const badgeIcon = computed(() => { switch (projectState.value) { - case ProjectState.SharedPersonal: + case ProjectState.Owned: case ProjectState.SharedOwned: - return 'user-friends'; + return 'user'; case ProjectState.Team: case ProjectState.SharedTeam: - return 'archive'; + return 'layer-group'; default: return ''; } @@ -81,6 +86,7 @@ const badgeTooltip = computed(() => { return i18n.baseText('projects.badge.tooltip.sharedOwned', { interpolate: { resourceTypeLabel: props.resourceTypeLabel, + count: numberOfMembersInHomeTeamProject.value, }, }); case ProjectState.SharedPersonal: @@ -88,6 +94,7 @@ const badgeTooltip = computed(() => { interpolate: { resourceTypeLabel: props.resourceTypeLabel, name: badgeText.value, + count: numberOfMembersInHomeTeamProject.value, }, }); case ProjectState.Personal: @@ -109,6 +116,7 @@ const badgeTooltip = computed(() => { interpolate: { resourceTypeLabel: props.resourceTypeLabel, name: badgeText.value, + count: numberOfMembersInHomeTeamProject.value, }, }); default: @@ -118,14 +126,53 @@ const badgeTooltip = computed(() => { - + diff --git a/packages/editor-ui/src/plugins/i18n/locales/en.json b/packages/editor-ui/src/plugins/i18n/locales/en.json index be7ff266f7..54618e5dc2 100644 --- a/packages/editor-ui/src/plugins/i18n/locales/en.json +++ b/packages/editor-ui/src/plugins/i18n/locales/en.json @@ -71,7 +71,7 @@ "generic.seePlans": "See plans", "generic.loading": "Loading", "generic.and": "and", - "generic.ownedByMe": "Owned by me", + "generic.ownedByMe": "(You)", "generic.moreInfo": "More info", "generic.next": "Next", "about.aboutN8n": "About n8n", @@ -2543,11 +2543,11 @@ "projects.move.resource.success.message": "{resourceName} {resourceTypeLabel} was moved to {targetProjectName}. {workflow} {link}", "projects.move.resource.success.message.workflow": "Please double check any credentials this workflow is using are also shared with {targetProjectName}.", "projects.move.resource.success.link": "View {targetProjectName}", - "projects.badge.tooltip.sharedOwned": "This {resourceTypeLabel} is owned by you and shared with one or more projects or users", - "projects.badge.tooltip.sharedPersonal": "This {resourceTypeLabel} is owned by {name} and shared with one or more projects or users", + "projects.badge.tooltip.sharedOwned": "This {resourceTypeLabel} is owned by you and shared with {count} users", + "projects.badge.tooltip.sharedPersonal": "This {resourceTypeLabel} is owned by {name} and shared with {count} users", "projects.badge.tooltip.personal": "This {resourceTypeLabel} is owned by {name}", "projects.badge.tooltip.team": "This {resourceTypeLabel} is owned and accessible by the {name} project.", - "projects.badge.tooltip.sharedTeam": "This {resourceTypeLabel} is owned and accessible by the {name} project and shared with one or more projects or users", + "projects.badge.tooltip.sharedTeam": "This {resourceTypeLabel} is owned by the {name} project and accessible by {count} users", "mfa.setup.invalidAuthenticatorCode": "{code} is not a valid number", "mfa.setup.invalidCode": "Two-factor code failed. Please try again.", "mfa.code.modal.title": "Two-factor authentication",